splent_feature_nginx
Nginx reverse proxy for SPLENT products (config archetype). It ships no Python behaviour of its own: it brings an nginx container that sits in front of the product's web service and answers on the product's HTTP and HTTPS ports.
What it provides
- Docker:
docker/docker-compose.dev.yml, nginx in front of the development server.docker/docker-compose.prod.yml, built fromdocker/Dockerfile.prod, with thecertbot_webrootandletsencryptvolumes for TLS certificates. Both joinsplent_network, where they reach the product asNGINX_UPSTREAM_HOST:NGINX_UPSTREAM_PORT.
- Server templates in
docker/nginx/templates/, one per environment, rendered by the nginx image at start. Both do the same things:- The web container is resolved per request through Docker's DNS
(
resolver 127.0.0.11 valid=10s,proxy_passthrough a variable), so nginx starts before the web container exists, answers 502 while it is away and follows it when it is recreated with another address. - Only
NGINX_SERVER_NAMEreaches the app. Adefault_servertakes every other Host, and requests without one: production closes the connection (444), development answers 421 with a sentence saying what to add. - The site's other names,
NGINX_REDIRECT_SERVER_NAMES(the bare domain of awww.site), get a 301 to the first name ofNGINX_SERVER_NAME, the canonical one, path and query included, and never reach the app. - The app is told the host as
X-Forwarded-Host: $host, the name the request matched a server by. A trusted terminator chooses the scheme and names the client, but not the host, so the app only ever hears a name ofNGINX_SERVER_NAME. - A server on
127.0.0.1:8080, inside the container only, answers/healthzwith 200, which the compose healthcheck asks. - The requests that publish to Zenodo or build a dataset's ZIP get
NGINX_LONG_REQUEST_TIMEOUT(600 s), everything else 120 s. /login,/signup,/reset/forgotand/captcha/*are rate limited per client address (NGINX_AUTH_RATE_LIMIT,NGINX_AUTH_RATE_BURST), with 429 above the limit.- Every request gets an id,
$request_idor the one a trusted proxy sent, which goes to the app and back to the browser asX-Request-ID. - The access log uses the
splentformat: the combined layout with the tokens of the reset, confirmation and sign-up links, the secret query parameters (token,api_key,code,state, ...) and the same in the Referer replaced by***, then the request id, the peer and the times. nginx's error log has no format, so the request line of a request nginx itself failed (the app down, a 429) is still written there as it came.tests/unit/test_server_templates.pyrenders both as the image does and checks each of these.
- The web container is resolved per request through Docker's DNS
(
docker/nginx/locations/, where a product drops extralocationblocks.protected-files.conf.exampleshows how to serve files the app has authorised throughX-Accel-Redirect.- An entrypoint script,
15-splent-trusted-proxies.sh, that trustsX-Forwarded-Proto,X-Request-IDand the client inX-Forwarded-Foronly fromNGINX_TRUSTED_PROXY_CIDRS, and another,16-splent-redirects.sh, that writes the redirect server forNGINX_REDIRECT_SERVER_NAMESand stops the container on a name that is not a host. - An
nginxentry in the Administration section of the menu registry, for administrators only. It opens the site through the proxy, atNGINX_SERVER_NAME(its first name that is a host) onNGINX_HTTP_HOST_PORT, in a new tab. It needssplent_framework>=1.21.0. - For one more minor, the same link in the signed-in sidebar slot
(
layout.authenticated_sidebar), atlocalhostas before, for the shells that still draw slots instead of the registry.
Configuration
| Env var | Default | Purpose |
|---|---|---|
NGINX_UPSTREAM_HOST |
__PRODUCT___web |
The product's web container, resolved to the product name by product:env --merge and product:build. |
NGINX_UPSTREAM_PORT |
5000 |
Port the web container listens on. |
NGINX_HTTP_HOST_PORT |
80 |
Host port for HTTP. Gets the product's port offset. |
NGINX_HTTPS_HOST_PORT |
443 |
Host port for HTTPS. Gets the product's port offset. |
NGINX_SERVER_NAME |
localhost, asked for in production |
server_name of the site, several names separated by spaces. The first is the canonical one. Any other Host is refused. |
NGINX_REDIRECT_SERVER_NAMES |
empty | Other names of the site, separated by spaces, redirected for good to the first name of NGINX_SERVER_NAME. |
NGINX_TRUSTED_PROXY_CIDRS |
empty | Peers allowed to set the forwarded headers and name the client. Empty trusts nobody. Set it only behind a TLS terminator on the host. |
NGINX_BIND_HOST |
0.0.0.0 |
Host interface the ports are published on. 127.0.0.1 behind a TLS terminator on the host. |
NGINX_AUTH_RATE_LIMIT |
60r/m |
Sustained rate per client address on the account forms. |
NGINX_AUTH_RATE_BURST |
30 |
Requests above that rate served at once before the 429s. |
NGINX_LONG_REQUEST_TIMEOUT |
600s |
Read timeout of the Zenodo publications and the ZIP downloads. Keep GUNICORN_TIMEOUT a little above it. |
docker/.env.prod.example is the production copy of docker/.env.example:
it asks for NGINX_SERVER_NAME, since localhost there would refuse the
real site, and marks the rest as the operator's.
Behind a TLS terminator
The server listens on HTTP only, so HTTPS comes from a terminator on the
host (nginx, Caddy, Traefik) that forwards to NGINX_HTTP_HOST_PORT. Then:
NGINX_BIND_HOST=127.0.0.1, so the terminator is the only way in.NGINX_TRUSTED_PROXY_CIDRS=172.16.0.0/12, the docker range the terminator arrives from (thepeer=field of the access log shows the address; add192.168.65.0/24on Docker Desktop).- The terminator keeps the Host it received and sets
X-Forwarded-For,X-Forwarded-Protoand, when it has one,X-Request-ID. WhateverX-Forwarded-Hostit sends is not passed on: nginx forwards the Host the request matched. - The app keeps
SPLENT_TRUSTED_PROXIES=1. nginx is the only hop it talks to: nginx decides whether to believe the terminator and forwards oneX-Forwarded-For(the client), oneX-Forwarded-Protoand oneX-Forwarded-Host. With 2 the app looks for two values in each, finds one, and ignores them:http://links, cookies withoutSecure, no HSTS.
When nginx is the edge, leave the first two as they are and keep
SPLENT_TRUSTED_PROXIES=1 as well.
One canonical host
A site reached by more than one name should answer by one, so that the
links it mails, the addresses a search engine keeps and the OAuth callbacks
all name the same host. Put the canonical name in NGINX_SERVER_NAME and
the others in NGINX_REDIRECT_SERVER_NAMES:
NGINX_SERVER_NAME=www.hub.example.org
NGINX_REDIRECT_SERVER_NAMES=hub.example.org
The app then only ever sees www.hub.example.org. Behind nginx it should
refuse any other Host too (Flask's TRUSTED_HOSTS) and build the links it
makes outside a request with SERVER_NAME, which is the product's to set.
Use it in a product
splent feature:add splent_feature_nginx
Metadata
Release files for splent-feature-nginx 1.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| splent_feature_nginx-1.5.0.tar.gz | 21.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| splent_feature_nginx-1.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 42.8 kB
Release files / splent_feature_nginx-1.5.0.tar.gz
| Download URL | splent_feature_nginx-1.5.0.tar.gz |
|---|---|
| Size | 21.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6073380fe263bc6c7001282eba0ad6e586deec3a0d74ba7b06db2c8426b1a772
|
|
BLAKE2b-256 checksum How to use checksums |
a0194d9377d2f5156d72649f80417ae77495e11806f1aed2e36491bfc0dc7456
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.16
|
Release files / splent_feature_nginx-1.5.0-py3-none-any.whl
| Download URL | splent_feature_nginx-1.5.0-py3-none-any.whl |
|---|---|
| Size | 21.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8d0a295a2d65d3e6c5a7fea4bd9d8e5b8384edc94f7a5ac72722619283f8ab89
|
|
BLAKE2b-256 checksum How to use checksums |
1b13df371930886c0133dab9c9891c28a1afcd100de7d0ce28f6de0e685c836a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.16
|
Security for splent-feature-nginx 1.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.