Question

WebSocket connection to 'wss://api-such.andsuch.xyz/graphql/' failed: Error during WebSocket handshake: Unexpected response code: 400

Hello everyone! I recently deployed a project I’m working on to production. I use DjangoChannelsGraphqlWs for GraphQL subscription functionalities. and I have GraphQL Playground set up via django-graphql-playground. Everything works fine locally - there are no issues whatsoever - subscriptions work fine. However, when I deployed I get the error below when I hit the Play button in Playground:

{
  "error": "Could not connect to websocket endpoint wss://api-such.andsuch.xyz/graphql/. Please check if the endpoint url is correct."
}

…and in my browser console, it says

WebSocket connection to 'wss://api-such.andsuch.xyz/graphql/' failed: Error during WebSocket handshake: Unexpected response code: 400

One thing to note is that the application is dockerized. Could it be from there? I don’t think so because it works locally. Here’s what my docker-compose file looks like:

version: '3.7'

services:
  nginx:
    container_name: nginx
    image: nginx
    restart: always
    depends_on:
      - web
    volumes:
      - ./web/dev.nginx.template:/etc/nginx/conf.d/dev.nginx.template
      - ./web/static:/static
      - ./web/media:/media
    ports:
      - "8080:80"
    networks:
      - RIO_NETWORK
    command: /bin/bash -c "envsubst \"`env | awk -F = '{printf \" $$%s\", $$1}'`\" < /etc/nginx/conf.d/dev.nginx.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"

  web:
    container_name: web
    restart: always
    build: ./web
    networks:
      - RIO_NETWORK
    depends_on:
      - postgres
      - redis
    volumes:
      - ./web:/usr/src/app/
    environment:
      - SECRET_KEY=enteryoursecretkey
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - TWILIO_ACCOUNT_SID=AC3c9e65abfaba8f446f6c6b3ad7553752
      - TWILIO_AUTH_TOKEN=0e66ca6bfe00e72542beb5fbf9f747f3
      - GBN_USERNAME=smsleak
      - GBN_PASSWORD=smsleak
      - DEBUG=True
      - MAILJET_API_KEY=97feac351b0184555c3cbb6374671c52
      - MAILJET_SECRET_KEY=913d594e24564d6c37c2faa2acf4740e
      - GRAPHQL_ENDPOINT=https://api-rio-dev.cnpro.xyz/graphql/
    command: bash -c /start.sh

  postgres:
    container_name: postgres
    restart: always
    image: postgres:latest
    networks:
      - RIO_NETWORK
    volumes:
      - pgdata:/var/lib/postgresql/data/

  redis:
   container_name: redis
   restart: always
   image: redis:latest
   networks:
    - RIO_NETWORK
   ports:
     - "6379:6379"
   volumes:
     - redisdata:/data

volumes:
  pgdata:
  redisdata:

networks:
  RIO_NETWORK:
    name: RIO_NETWORK
    driver: bridge

settings.py

...
...
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            'hosts': [(os.getenv('REDIS_HOST', 'redis'), os.getenv('REDIS_PORT', 6379))],
        }
    }
}
...
...

urls.py

...
...
from graphql_playground.views import GraphQLPlaygroundView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('playground/', GraphQLPlaygroundView.as_view(
        endpoint=os.getenv('GRAPHQL_ENDPOINT'))),
]
...

nginx

server {
    client_max_body_size 10M; 
    
    listen 443 ssl;
    listen [::]:443 ssl;
    
    server_name api-such.andsuch.xyz;
    
    ssl_certificate /etc/ssl/certs/andsuch.xyz.pem;
    ssl_certificate_key /etc/ssl/certs/andsuch.xyz.key;
    
    location = /favicon.ico { access_log off; log_not_found off; }
    
    location / {
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection ‘upgrade’;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://0.0.0.0:8080;
    }
}

What could be wrong? I’m outta ideas. Thanks!

Show comments

Submit an answer


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Sign In or Sign Up to Answer

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

Accepted Answer

After going through lots of articles, I discovered this one, and this section made me realize the only thing that was wrong was the fact that I didn’t expose port 8000 internally to other docker services. In my docker-compose file, the web service was supposed to have…

...
...
expose:
    - 8000
...
...

… in it. I added that and it was solved. Thanks for all the assistance @audebertc @niloteixeira

Change the settings.py DEBUG to True, then Django should give you more information regarding the bad request. In my case it was an incorrect ALLOWED_HOSTS in settings.py that debug mode showed info enough to fix it.

you must proxying websocket to pass docker…

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.