Dynamic Applications and Reverse Proxies
July 16, 2026 ยท View on GitHub
A dynamic application creates responses using code, data, authentication, or user input. It requires more operational controls than a static site.
A Common Architecture
Browser
|
| HTTPS :443
v
Nginx reverse proxy
|
| HTTP on 127.0.0.1:3000
v
Application process
|
v
Database or external services
The reverse proxy handles the public connection and forwards selected requests to an application bound to the local interface.
Why Use a Reverse Proxy
A reverse proxy can:
- Terminate TLS.
- Route multiple hostnames or paths.
- Apply request size and timeout limits.
- Serve static files efficiently.
- Add standard headers.
- Keep the application port off the public network.
It does not automatically make an insecure application safe.
Bind the Application Locally
Prefer:
127.0.0.1:3000
over:
0.0.0.0:3000
when only the reverse proxy should reach the application. Confirm the real listener:
sudo ss -lntp
Example Nginx Location
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
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_connect_timeout 5s;
proxy_read_timeout 30s;
}
Header trust is an application decision. Configure the application to trust proxy headers only from the actual proxy, or clients may forge scheme and address information.
Application Process Management
A production application needs:
- A non-root service account
- A defined working directory
- Environment or secret loading
- Automatic restart with limits
- Structured logs
- Health checks
- Graceful shutdown
- A deployment and rollback procedure
Do not use a development server as a permanent production process unless its documentation explicitly supports production operation.
Secrets and Configuration
Separate configuration into:
Public configuration: ports, feature flags, public URLs
Sensitive configuration: database passwords, signing keys, API tokens
The frontend bundle is public. A secret embedded in browser JavaScript is no longer secret.
Database Safety
If the application uses a database:
- Bind it to a private interface unless remote access is required.
- Use a dedicated database role with limited permissions.
- Apply schema migrations through a reviewed process.
- Back up data before migrations.
- Test restoration, not only backup creation.
- Avoid logging passwords, tokens, or full personal records.
Uploads and User Input
Treat all user input as untrusted.
- Validate type, length, and expected format on the server.
- Generate storage names instead of trusting uploaded filenames.
- Store uploads outside executable application directories.
- Set size limits.
- Escape output according to its HTML, URL, JavaScript, shell, or SQL context.
- Use parameterized database queries.
Client-side validation improves usability but is not a security boundary.
Health Checks
Create a lightweight endpoint that proves the application can serve requests without exposing secrets:
GET /healthz -> 200 OK
Decide whether a health check verifies only the process or also dependencies such as the database. Deep checks can create load or expose dependency failures to the public.
Diagnose a 502 Response
- Confirm the application process is running.
- Confirm the expected local port is listening.
- Request the application directly from the server.
- Check reverse-proxy error logs.
- Check application logs.
- Verify protocol, address, and port in
proxy_pass. - Check local firewall or security policy.
Example:
curl -I http://127.0.0.1:3000/healthz
sudo tail -n 100 /var/log/nginx/example.dpdns.org.error.log
Deployment Exercise
Draw a dynamic application architecture and label:
- Public ports
- Private ports
- TLS termination
- Secret storage
- Logs
- Backup boundary
- Health check
- Rollback target
Do not deploy it until every label has an owner.
Continue to Accessibility and Search Basics.