Use simple HAProxy configuration hacks to serve a single static file.
HAProxy version.
$ sudo haproxy -v HA-Proxy version 1.8.19-1 2019/02/12 Copyright 2000-2019 Willy Tarreau <willy at haproxy.org>
Serve single static file
This is the simplest case. HAProxy will not log these requests.
Create /etc/haproxy/static/index.static.html
HTML file including HTTP headers.
HTTP/1.0 200 OK Cache-Control: no-cache Connection: close Content-Type: text/html <html> <body> <p>Static file</p> </body> </html>
Use the following HAProxy configuration.
global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners stats timeout 30s user haproxy group haproxy daemon # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # Default ciphers to use on SSL-enabled listening sockets. # For more information, see ciphers(1SSL). This list is from: # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ # An alternative list with additional directives can be obtained from # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS ssl-default-bind-options no-sslv3 defaults log global mode http option httplog option dontlognull timeout connect 5000 timeout client 50000 timeout server 50000 errorfile 400 /etc/haproxy/errors/400.http errorfile 403 /etc/haproxy/errors/403.http errorfile 408 /etc/haproxy/errors/408.http errorfile 500 /etc/haproxy/errors/500.http errorfile 502 /etc/haproxy/errors/502.http errorfile 503 /etc/haproxy/errors/503.http errorfile 504 /etc/haproxy/errors/504.http listen example-single-static-file bind 127.0.0.1:7000 mode http monitor-uri /static/ errorfile 200 /etc/haproxy/static/index.static.html
Reload HAProxy service.
$ sudo systemctl reload haproxy
Verify the URL of the static file.
$ curl -v -w "* Received HTTP status code: %{http_code}\n" http://127.0.0.1:7000/static/ * Expire in 0 ms for 6 (transfer 0x55f8e8f21f50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x55f8e8f21f50) * Connected to 127.0.0.1 (127.0.0.1) port 7000 (#0) > GET /static/ HTTP/1.1 > Host: 127.0.0.1:7000 > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Cache-Control: no-cache < Connection: close < Content-Type: text/html < <html> <body> <p>Static file</p> </body> </html> * Closing connection 0 * Received HTTP status code: 200
Anything else will end up returning 503 Service Unavailable
HTTP status code.
$ curl -v -w "* Received HTTP status code: %{http_code}\n" http://127.0.0.1:7000/ * Expire in 0 ms for 6 (transfer 0x562ba6b70f50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x562ba6b70f50) * Connected to 127.0.0.1 (127.0.0.1) port 7000 (#0) > GET / HTTP/1.1 > Host: 127.0.0.1:7000 > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 503 Service Unavailable < Cache-Control: no-cache < Connection: close < Content-Type: text/html < <html><body><h1>503 Service Unavailable</h1> No server is available to handle this request. </body></html> * Closing connection 0 * Received HTTP status code: 503
Serve multiple static files
This is more interesting as it requires a slightly different approach. HAProxy will log these requests using 503
HTTP status code, so it needs to be silenced.
Create /etc/haproxy/static/location.txt
text file, including HTTP headers.
HTTP/1.0 200 OK Cache-Control: no-cache Connection: close Content-Type: text/plain location: eu
Create /etc/haproxy/static/serverid.txt
text file, including HTTP headers.
HTTP/1.0 200 OK Cache-Control: no-cache Connection: close Content-Type: text/plain serverid: kraken
Create /etc/haproxy/static/environment.txt
text file, including HTTP headers.
HTTP/1.0 200 OK Cache-Control: no-cache Connection: close Content-Type: text/plain environment: staging
Use the following HAProxy configuration.
global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners stats timeout 30s user haproxy group haproxy daemon # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # Default ciphers to use on SSL-enabled listening sockets. # For more information, see ciphers(1SSL). This list is from: # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ # An alternative list with additional directives can be obtained from # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS ssl-default-bind-options no-sslv3 defaults log global mode http option httplog option dontlognull timeout connect 5000 timeout client 50000 timeout server 50000 errorfile 400 /etc/haproxy/errors/400.http errorfile 403 /etc/haproxy/errors/403.http errorfile 408 /etc/haproxy/errors/408.http errorfile 500 /etc/haproxy/errors/500.http errorfile 502 /etc/haproxy/errors/502.http errorfile 503 /etc/haproxy/errors/503.http errorfile 504 /etc/haproxy/errors/504.http userlist static-auth-list user static insecure-password static frontend example-multiple-static-files bind 127.0.0.1:8000 mode http acl static-auth http_auth(static-auth-list) http-request auth realm static unless static-auth acl is-path-static-location-txt path /location.txt acl is-path-static-serverid-txt path /serverid.txt acl is-path-static-environment-txt path /environment.txt use_backend example-backend-location-txt if is-path-static-location-txt use_backend example-backend-serverid-txt if is-path-static-serverid-txt use_backend example-backend-environment-txt if is-path-static-environment-txt backend example-backend-location-txt mode http http-request set-log-level silent errorfile 503 /etc/haproxy/static/location.txt backend example-backend-serverid-txt mode http http-request set-log-level silent errorfile 503 /etc/haproxy/static/serverid.txt backend example-backend-environment-txt mode http http-request set-log-level silent errorfile 503 /etc/haproxy/static/environment.txt
Verify the location.txt
static file.
$ curl -v -w "* Received HTTP status code: %{http_code}\n" -u static:static http://127.0.0.1:8000/location.txt * Expire in 0 ms for 6 (transfer 0x55f2385c6f50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x55f2385c6f50) * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0) * Server auth using Basic with user 'static' > GET /location.txt HTTP/1.1 > Host: 127.0.0.1:8000 > Authorization: Basic c3RhdGljOnN0YXRpYw== > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Cache-Control: no-cache < Connection: close < Content-Type: text/plain < location: eu * Closing connection 0 * Received HTTP status code: 200
Verify the serverid.txt
static file.
$ curl -v -w "* Received HTTP status code: %{http_code}\n" -u static:static http://127.0.0.1:8000/serverid.txt * Expire in 0 ms for 6 (transfer 0x56356a20ef50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x56356a20ef50) * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0) * Server auth using Basic with user 'static' > GET /serverid.txt HTTP/1.1 > Host: 127.0.0.1:8000 > Authorization: Basic c3RhdGljOnN0YXRpYw== > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Cache-Control: no-cache < Connection: close < Content-Type: text/plain < serverid: kraken * Closing connection 0 * Received HTTP status code: 200
Verify the environment.txt
static file.
$ curl -v -w "* Received HTTP status code: %{http_code}\n" -u static:static http://127.0.0.1:8000/environment.txt * Expire in 0 ms for 6 (transfer 0x5634941faf50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x5634941faf50) * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0) * Server auth using Basic with user 'static' > GET /environment.txt HTTP/1.1 > Host: 127.0.0.1:8000 > Authorization: Basic c3RhdGljOnN0YXRpYw== > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Cache-Control: no-cache < Connection: close < Content-Type: text/plain < environment: staging * Closing connection 0 * Received HTTP status code: 200
Anything else will end up returning 503 Service Unavailable
HTTP status code or 401 Unauthorized
if user credentials are missing or incorrect.
$ curl -v -w "* Received HTTP status code: %{http_code}\n" -u static:static http://127.0.0.1:8000/other.txt * Expire in 0 ms for 6 (transfer 0x55fd8fe78f50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x55fd8fe78f50) * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0) * Server auth using Basic with user 'static' > GET /other.txt HTTP/1.1 > Host: 127.0.0.1:8000 > Authorization: Basic c3RhdGljOnN0YXRpYw== > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 503 Service Unavailable < Cache-Control: no-cache < Connection: close < Content-Type: text/html < <html><body><h1>503 Service Unavailable</h1> No server is available to handle this request. </body></html> * Closing connection 0 * Received HTTP status code: 503
$ curl -v -w "* Received HTTP status code: %{http_code}\n" http://127.0.0.1:8000/location.txt * Expire in 0 ms for 6 (transfer 0x55e7ad280f50) * Trying 127.0.0.1... * TCP_NODELAY set * Expire in 200 ms for 4 (transfer 0x55e7ad280f50) * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0) > GET /location.txt HTTP/1.1 > Host: 127.0.0.1:8000 > User-Agent: curl/7.64.0 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 401 Unauthorized < Cache-Control: no-cache < Connection: close < Content-Type: text/html < WWW-Authenticate: Basic realm="static" < <html><body><h1>401 Unauthorized</h1> You need a valid user and password to access this content. </body></html> * Closing connection 0 * Received HTTP status code: 401
These examples should give you some ideas on how to use the configuration hack mentioned above.