Horizon

Share a Django dev server

Share a Django dev server with a Horizon tunnel, and fix the "CSRF verification failed" error on POST forms with CSRF_TRUSTED_ORIGINS.

Share your Django runserver with a public HTTPS URL, with POST forms and the admin login working.

Before you begin

  • Node.js 18 or later
  • A Horizon account and the CLI (see Getting started)
  • A reserved subdomain for -s. Reserve one on the Subdomains page.
  • A Django project you can run with python manage.py runserver

Start the dev server

Run Django on its default port, 8000.

python manage.py runserver

Start a tunnel

Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so the setting in the next step would stop matching. Reserved subdomains are a paid feature, see Pricing.

hrzn tunnel http://localhost:8000 -s my-app
Output
HORIZON: Tunnel connected
  URL          https://my-app.hrzn.run (reserved)
  Forwarding   http://localhost:8000
  Request log  https://hrzn.run/dashboard/tunnels/my-app

Your public URL is https://my-app.hrzn.run. Keep this terminal open.

Trust the public origin for POST requests

The CLI sets the Host header to localhost:8000, so Django's ALLOWED_HOSTS check passes. With DEBUG = True and an empty ALLOWED_HOSTS, Django accepts localhost.

POST forms need one more setting. Browsers send an Origin header with the public address, https://my-app.hrzn.run. Django compares Origin with the current host and with CSRF_TRUSTED_ORIGINS. The host is localhost:8000, so the comparison fails unless you list the public origin. Each entry needs its scheme.

settings.py
CSRF_TRUSTED_ORIGINS = ["https://my-app.hrzn.run"]

Restart runserver after you edit the file.

Check it works

Open https://my-app.hrzn.run in a browser. On the first visit Horizon shows a Before you continue page. Select Continue to site.

Then submit any form that uses {% csrf_token %}, such as the admin login at /admin/. The Horizon terminal prints one line per request:

Output
  GET     200  /admin/login/
  POST    302  /admin/login/

Troubleshooting

CSRF verification failed. Request aborted.

Django shows this page with a 403 status. The reason line says what failed.

  • Origin checking failed - https://my-app.hrzn.run does not match any trusted origins. means CSRF_TRUSTED_ORIGINS is missing or doesn't match. Add "https://my-app.hrzn.run" with the scheme, and restart runserver.
  • CSRF cookie not set. means the browser didn't send the cookie. Open the app through the public URL only, not a mix of localhost:8000 and the public URL.
  • If you started the tunnel without -s, the subdomain changed. Update CSRF_TRUSTED_ORIGINS, or use "https://*.hrzn.run" to match any subdomain.

Invalid HTTP_HOST header

The full message reads Invalid HTTP_HOST header: 'my-app.hrzn.run'. You may need to add 'my-app.hrzn.run' to ALLOWED_HOSTS. You only see it if you set USE_X_FORWARDED_HOST = True, because Django then reads the public host from X-Forwarded-Host. Add the public host to ALLOWED_HOSTS, or remove the setting.

Absolute URLs point to localhost

Django builds absolute URLs, such as request.build_absolute_uri(), from Host, which is localhost:8000. Set USE_X_FORWARDED_HOST = True and add the public host to ALLOWED_HOSTS to use the public host.

Nothing reaches your app

  • Check that the Horizon terminal is still running. If its last line is Connection lost. Reconnecting…, wait for Reconnected.
  • Check that runserver listens on port 8000, the port in your hrzn tunnel command.

Next steps

On this page