Run your own app¶
Most tutorials assume the app and your browser are on the same computer, so
they say "open http://localhost:5000." On your lab that isn't true — your
browser reaches the app through the lab gateway. Three small settings make your
app work here. You're changing settings, never your app's actual code.
Don't want to do this by hand?
Copy a prompt from AI prompts to fix your app and let your AI assistant make these changes for you.
The three things to set¶
1. Let your app accept outside connections¶
This is the one that trips up almost everyone. By default, lots of apps start in
a private mode where only the same computer can open them. The gateway is a
different machine, so it can't get in — and you see a "Nothing on port N"
page. The fix: start your app on 0.0.0.0 instead of localhost.
Not sure which mode your app is in? Run this in a terminal on the lab computer (use your port number):
0.0.0.0:5000→ good, the gateway can reach it.127.0.0.1:5000→ private mode; this is the problem. Switch it to0.0.0.0.- nothing printed → the app isn't running (or it's on a different port).
The technical version (optional)
0.0.0.0 means "listen on all network interfaces"; 127.0.0.1
(localhost) is the loopback interface, reachable only from the VM itself.
The reverse proxy connects from another host, so a loopback-only bind
refuses its connection and nginx returns the "Nothing on port N" page.
2. Use an allowed port¶
Pick a port the gateway can route to — see
How to open your apps. Port 5000 opens at app-lab1234…;
any port from 2000 to 9000 opens at <port>-lab1234….
3. Expect a secure web address¶
Your app is opened over https:// and is handed its real web address
(app-lab1234.labs.decoded.com), not localhost. Two things may need a nudge:
- Some apps keep a guest list of web addresses they trust. If yours does,
add
app-lab1234.labs.decoded.comto it (or, while you're just developing, allow any address). Otherwise you may get a blank page or an "invalid host" message. - Tell your app it's being opened securely, so the links and logins it
builds start with
https://and nothttp://. In a website's front-end code, point at your own API with plain paths like/api/..., nothttp://localhost:5000/....
Live-reload (the page refreshing itself as you edit) works here too. If it doesn't, it's usually the same "secure address" nudge — the recipes below set it where it's needed.
The technical version (optional)
The proxy passes your app the public hostname in the Host header, so
frameworks with a host allow-list (Vite, Django, CRA, webpack) reject it
until you add that host. It also sets X-Forwarded-Proto: https; trust that
header (Flask ProxyFix, Express trust proxy, Django
SECURE_PROXY_SSL_HEADER) so generated URLs, redirects and secure cookies
use https. Dev-server hot-reload uses a WebSocket — point its client at
wss://<host> on port 443 (e.g. Vite hmr.clientPort: 443, CRA
WDS_SOCKET_PORT=443).
Find your tool below¶
Each tab shows the exact change, then the address to open. Replace lab1234
with your lab number. If a setting looks unfamiliar, that's fine — copy it as
shown; it does the three things above.
Flask — bind to 0.0.0.0, and trust the proxy so url_for(..., _external=True) builds https:// links:
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000) # (1)!
- Or from the CLI:
flask run --host 0.0.0.0 --port 5000. Flask's dev server has no host allowlist, so nothing else is needed.
FastAPI (uvicorn) — --proxy-headers makes it honour X-Forwarded-Proto:
Gunicorn:
→ Open https://app-lab1234.labs.decoded.com/ (port 5000).
runserver binds to one address; the rest is settings. The host allowlist
and CSRF_TRUSTED_ORIGINS matter — without the latter, admin login and
any form POST fail with a 403 over HTTPS.
# settings.py
ALLOWED_HOSTS = ["8000-lab1234.labs.decoded.com"] # or ["*"] in dev (1)
CSRF_TRUSTED_ORIGINS = ["https://8000-lab1234.labs.decoded.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
["*"]is fine for a throwaway dev VM but never for anything you'd treat as production.
→ Open https://8000-lab1234.labs.decoded.com/. The host in the two
settings above must match the URL you open — to use app-lab1234… instead,
run on port 5000 and put that host there.
const app = require("express")();
app.set("trust proxy", 1); // honour X-Forwarded-Proto / -For
app.listen(5000, "0.0.0.0", () => console.log("listening on 0.0.0.0:5000"));
Express has no host allowlist, so binding + trust proxy is enough.
→ Open https://app-lab1234.labs.decoded.com/ (port 5000).
Vite checks which web address opened it, and its live-reload needs a small setting — both are handled here:
// vite.config.js
export default {
server: {
host: true, // bind 0.0.0.0
port: 5173,
allowedHosts: [".labs.decoded.com"], // (1)
hmr: { protocol: "wss", clientPort: 443 }, // hot-reload over the proxy
},
};
- The leading dot allows any
*.labs.decoded.comhost.allowedHosts: truealso works for dev.
→ Open https://5173-lab1234.labs.decoded.com/ (or set port: 5000 and
use app-lab1234…).
Next serves at the root path, so no assetPrefix is needed. If a recent
Next version warns about cross-origin dev requests, add the host:
→ Open https://3000-lab1234.labs.decoded.com/.
react-scripts checks the web address that opened it, and its live-reload
uses a background connection — set both with environment variables:
WDS_SOCKET_PORT=443 points hot-reload at the public port;
DANGEROUSLY_DISABLE_HOST_CHECK is a dev-only convenience.
→ Open https://3000-lab1234.labs.decoded.com/.
Streamlit:
streamlit run app.py \
--server.address 0.0.0.0 --server.port 8501 \
--server.headless true \
--server.enableCORS false --server.enableXsrfProtection false
→ https://8501-lab1234.labs.decoded.com/
Gradio:
→ https://7860-lab1234.labs.decoded.com/
Your own Jupyter (the platform already gives you one at nb-lab1234…):
jupyter lab --ip 0.0.0.0 --port 8888 --no-browser \
--ServerApp.allow_origin='*' --ServerApp.trust_xheaders=True
→ https://8888-lab1234.labs.decoded.com/
Any server works as long as it binds 0.0.0.0 on a 2000–9000 port. A plain
static server:
→ https://8000-lab1234.labs.decoded.com/
For anything else, the rule is the same: find its "host"/"bind"/"address"
setting and set it to 0.0.0.0; find its port and make it 2000–9000; if it
has a host/allowed-hosts check, add *.labs.decoded.com.
One safety note¶
These addresses can be opened by anyone who knows them — there's no login in front of your app unless you add one. Don't put a database, an admin panel, or anything holding secrets on an open port without protecting it. If you're not sure, ask your instructor.
Stuck on a specific symptom? See Troubleshooting.