Serve and deploy real web apps: nginx, ports, reverse proxies and rollouts.
- Allow Cross-Origin Requestseasy · in-browser
Write /etc/nginx/conf.d/cors.conf with a location /api/ that adds CORS response headers: add_header Access-Control-Allow-Origin * and add_header Access-Control-Allow-Methods listing at least GET and POST.
(Authored config, graded structurally — the engine isn't run here.)
- Apache Name-Based VHosteasy · in-browser
Write /etc/apache2/sites-available/example.conf: an Apache <VirtualHost *:80> block with ServerName www.example.com and DocumentRoot /var/www/example, closed with </VirtualHost>.
(Authored config, graded structurally — the engine isn't run here.)
- Author a robots.txteasy · in-browser
Write /var/www/html/robots.txt: a robots file with a User-agent: * rule, a Disallow: /admin rule, and a Sitemap: line pointing at an http(s) URL.
(Authored config, graded structurally — the engine isn't run here.)
- Listen on 8090easy
Configure nginx to answer 200 at http://127.0.0.1:8090/.
- Point current at a releaseeasy
Create a symlink /srv/app/current pointing to /srv/app/releases/v2.
- Raise the Upload Size Limiteasy · in-browser
Write /etc/nginx/conf.d/upload.conf with a location /upload that sets client_max_body_size 50m; and proxies to an upload backend with proxy_pass (any http:// target).
(Authored config, graded structurally — the engine isn't run here.)
- Redirect to an external URLeasy
GET http://127.0.0.1/go must 301-redirect to https://example.com/.
- Return a literal bodyeasy
Make GET http://127.0.0.1/ping return 200 with the exact body pong.
- Serve a static siteeasy
Get nginx serving on port 80 so that GET http://127.0.0.1/ returns a page whose body contains WELCOME. The content is at /srv/site/index.html.
- Serve on a non-standard porteasy
Configure nginx to listen on port 8081 and return any 200 response at http://127.0.0.1:8081/.
- Add Hardening Response Headersmedium · in-browser
Write /etc/nginx/conf.d/headers.conf with a server{} that emits four security response headers via add_header: X-Frame-Options SAMEORIGIN, X-Content-Type-Options nosniff, Strict-Transport-Security with a max-age, and a Referrer-Policy header.
(Authored config, graded structurally — the engine isn't run here.)
- Add a 301 redirectmedium
Configure nginx so that GET http://127.0.0.1/old responds with an HTTP 301 redirect to /new.
- Add a response headermedium
Make nginx send the header `X-Powered-By: tmpfs` on responses to http://127.0.0.1/.
- Allow only GETmedium
A POST to http://127.0.0.1/ must return 405 (only GET allowed).
- Block dotfilesmedium
Requests to hidden files must 403, e.g. http://127.0.0.1/.secret returns 403.
- Bring the web server back onlinemedium
The marketing site should be served by nginx on port 80 and return
the exact body "tmpfs-academy OK" at GET /healthz. Right now the
service is down and something in its configuration is wrong.
Diagnose and fix it. Your fix must also survive a reboot — the
service has to come back on its own.
When you think it's solved, click "Check".
- Cap Concurrent Connectionsmedium · in-browser
Write /etc/nginx/conf.d/connlimit.conf declaring a connection-limit zone with limit_conn_zone $binary_remote_addr zone=addr:10m; and, inside a server{} or location, applying limit_conn addr 10; to cap each client to 10 simultaneous connections.
(Authored config, graded structurally — the engine isn't run here.)
- Custom 404 bodymedium
Configure nginx so a request to a missing path returns status 404 with a body containing the word GONE.
- Custom 500 pagemedium
http://127.0.0.1/boom must return 500 with a body containing OOPS.
- Enable directory listingmedium
Serve an autoindex listing at http://127.0.0.1/files/ (body contains 'Index of').
- Enable gzip compressionmedium
Enable gzip so a request advertising gzip support gets a gzip-encoded response from http://127.0.0.1/.
- Forbidden locationmedium
http://127.0.0.1/private must return 403.
- Force a content typemedium
GET http://127.0.0.1/raw must return Content-Type text/plain.
- Forward the Real Client Infomedium · in-browser
Write /etc/nginx/conf.d/proxy-headers.conf with a location / that proxy_passes to a backend and sets the four standard forwarding headers: proxy_set_header Host $host;, proxy_set_header X-Real-IP $remote_addr;, proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;, and proxy_set_header X-Forwarded-Proto $scheme;.
(Authored config, graded structurally — the engine isn't run here.)
- Get nginx servingmedium
Install and run nginx so that http://127.0.0.1/ returns the body "OK". nginx must be enabled to start on boot.
- Hide nginx versionmedium
The Server response header must not leak the version (server_tokens off → `Server: nginx`).
- JSON endpointmedium
http://127.0.0.1/api must return application/json and the body {"ok":true}.
- Load-Balance an Upstream Poolmedium · in-browser
Write /etc/nginx/conf.d/lb.conf defining an upstream block named app_pool that uses the least_conn balancing method and contains two backends, 10.0.0.11:8080 and 10.0.0.12:8080. Add a server{} with a location / that does proxy_pass http://app_pool;.
(Authored config, graded structurally — the engine isn't run here.)
- Move nginx to 8080medium
Configure nginx to listen on port 8080 and return "PORT8080" at /.
- Protect a path with basic authmedium
Require HTTP Basic auth on /private (an unauthenticated request to http://127.0.0.1/private must return 401).
- Proxy a WebSocket Endpointmedium · in-browser
Write /etc/nginx/conf.d/ws.conf with a location /ws/ that proxy_passes to a backend and enables WebSocket upgrades: proxy_http_version 1.1;, proxy_set_header Upgrade $http_upgrade;, and proxy_set_header Connection "upgrade";.
(Authored config, graded structurally — the engine isn't run here.)
- Serve a path via aliasmedium
http://127.0.0.1/docs/readme must serve the file containing DOCROOT.
- Set Cache-Controlmedium
Responses to / must include Cache-Control: max-age=3600.
- Systemd Unit for a Gunicorn Appmedium · in-browser
Write /etc/systemd/system/webapp.service: a systemd service whose first line is [Unit], with a [Service] section that sets WorkingDirectory=/srv/app and an ExecStart that launches gunicorn, and an [Install] section with WantedBy=multi-user.target.
(Authored config, graded structurally — the engine isn't run here.)
- Terminate TLS with a Certmedium · in-browser
Write /etc/nginx/conf.d/tls.conf with a server{} that does listen 443 ssl;, points ssl_certificate to /etc/ssl/certs/site.crt and ssl_certificate_key to /etc/ssl/private/site.key, and restricts ssl_protocols to include at least TLSv1.3.
(Authored config, graded structurally — the engine isn't run here.)
- Deploy a Python servicepro
Create and enable a systemd service that serves /srv/api on port 8000. http://127.0.0.1:8000/ must return the body "API-LIVE".
- Expose stub_statuspro
http://127.0.0.1/nginx_status must return 200 containing 'Active connections'.
- Force HTTPSpro
Plain http://127.0.0.1/ must 301-redirect to https.
- HAProxy Frontend and Backendpro · in-browser
Write /etc/haproxy/haproxy.cfg with a frontend named http_in that binds *:80 and uses default_backend web_servers, plus a backend named web_servers that uses balance roundrobin and lists two servers named web1 and web2.
(Authored config, graded structurally — the engine isn't run here.)
- Hand PHP to php-fpmpro · in-browser
Write /etc/nginx/conf.d/php.conf with a regex location matching .php files (location ~ \.php$) that includes fastcgi_params, sets fastcgi_pass unix:/run/php/php-fpm.sock;, and passes fastcgi_param SCRIPT_FILENAME to the FPM worker.
(Authored config, graded structurally — the engine isn't run here.)
- Map User-Agent to a Variablepro · in-browser
Write /etc/nginx/conf.d/mobile-map.conf using nginx's map directive: map $http_user_agent $is_mobile { ... } with a default 0; entry and a case-insensitive pattern (~*) matching android or iphone that sets the value to 1.
(Authored config, graded structurally — the engine isn't run here.)
- Rate-Limit the APIpro · in-browser
Write /etc/nginx/conf.d/ratelimit.conf that declares a request-rate limit zone with limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s; then, in a location /api/, applies it with limit_req zone=api burst=10 nodelay;.
(Authored config, graded structurally — the engine isn't run here.)
- Reverse-proxy a backendpro
A backend listens on 127.0.0.1:5000 and returns "BACKEND". Configure nginx so http://127.0.0.1/ (port 80) proxies to it and returns the same body.
- SPA fallback with try_filespro
Serve a single-page app: any path that isn't a real file must fall back to /index.html (body contains APPROOT).
- Serve HTTPS with a self-signed certpro
Serve HTTPS on 443 with a self-signed certificate so that `curl -k https://127.0.0.1/` returns 200.