diff --git a/html/images/openresty-splash-page.png b/html/images/openresty-splash-page.png
new file mode 100644
index 0000000..deb1443
Binary files /dev/null and b/html/images/openresty-splash-page.png differ
diff --git a/posts/2025-06-22-test-nginx-conf-directives.md b/posts/2025-06-22-test-nginx-conf-directives.md
new file mode 100644
index 0000000..7cdbbcc
--- /dev/null
+++ b/posts/2025-06-22-test-nginx-conf-directives.md
@@ -0,0 +1,415 @@
+$index
+
+## Introduction
+
+We'll need nginx and luarocks. Buildpack has luarocks installed.
+
+docker pull openresty/openresty:bookworm-buildpack
+
+$ docker run --rm -it -p 80:80 openresty/openresty:bookworm-buildpack
+
+Visit `localhost` in browser. Should see OpenResty splash page.
+
+
+
+https://openresty.org/en/getting-started.html#prepare-directory-layout
+
+$ mkdir -p logs/ conf/conf.d/ html/
+
+https://github.com/openresty/docker-openresty?tab=readme-ov-file#nginx-config-files
+
+$ docker run --rm -it -w /opt -v $PWD:/opt openresty/openresty:bookworm-buildpack \
+cp /etc/nginx/conf.d/default.conf /opt/conf.d/
+
+edit default.conf
+change `root /usr/local/openresty/nginx/html;` to:
+
+ root /var/www;
+
+`html/index.html`
+
+```html
+
+
+
+
+
+
+
+
+ hello world!
+
+
+```
+
+Start nginx:
+
+```console
+$ docker run --rm -it -p 80:80 \
+-v $PWD/conf/conf.d:/etc/nginx/conf.d -v $PWD/html:/var/www \
+openresty/openresty:bookworm-buildpack
+```
+
+Then, in another console:
+
+```console
+$ curl -v localhost
+* Trying 127.0.0.1:80...
+* Connected to localhost (127.0.0.1) port 80 (#0)
+> GET / HTTP/1.1
+> Host: localhost
+> User-Agent: curl/7.88.1
+> Accept: */*
+>
+< HTTP/1.1 200 OK
+< Server: openresty/1.27.1.2
+< ...
+<
+
+
+ ...
+
+ hello world!
+
+
+```
+
+If we wanted to write a test for that, we need some packages from `luarocks`.
+
+`Dockerfile`
+
+```Dockerfile
+FROM openresty/openresty:bookworm-buildpack
+
+WORKDIR /opt/app
+
+# needed for testing
+RUN luarocks install busted
+RUN luarocks install luajit-curl
+RUN luarocks install luasocket # needed for testing nginx reverse proxy
+```
+
+```console
+$ docker build -t test-nginx .
+$ mkdir spec
+```
+
+`spec/nginx_spec.moon`
+
+```moonscript
+http = require "luajit-curl-helper.http"
+
+req = (url) ->
+ request = http.init url
+ st = request\perform!
+ error request\lastError! if not st
+ request
+
+describe "http://localhost", ->
+ it "sends /index.html", ->
+ request = req "http://localhost"
+ assert.same request\statusCode!, 200
+ assert.same request\statusMessage!, "OK"
+ assert.same request\body!\match("%s+(.-)%s+"), "hello world!"
+```
+
+Start the test server:
+
+```console
+$ ct=$(docker run --rm -d \
+-v $PWD/conf/conf.d:/etc/nginx/conf.d \
+-v $PWD/html:/var/www \
+-v $PWD:/opt/app \
+test-nginx)
+```
+
+Run the tests.
+
+```console
+$ docker exec -t $ct busted
+●
+1 success / 0 failures / 0 errors / 0 pending : 0.008246 seconds
+```
+
+Stop the test server.
+
+```console
+$ docker exec -t $ct openresty -s stop
+```
+
+## Edit hosts
+
+Instead of `localhost` we'd like to use an actual domain name. We can do this
+with the `--add-host` option. But before we do that, we want to make sure our
+container does not have access to the internet, otherwise we might
+unintentionally get a response from a domain's server on the internet rather
+than from our test server.
+
+$ docker network create --internal no-internet
+
+Now we can start the test server with our host:
+
+```console
+$ ct=$(docker run --rm -d \
+-v $PWD/conf/conf.d:/etc/nginx/conf.d \
+-v $PWD/html:/var/www \
+-v $PWD:/opt/app \
+--network no-internet \
+--add-host=domain.abc=127.0.0.1 \
+test-nginx)
+```
+
+Update our test
+
+ request = req "http://localhost"
+
+to
+
+ request = req "http://domain.abc"
+
+Run the tests.
+
+```console
+$ docker exec -t $ct busted
+●
+1 success / 0 failures / 0 errors / 0 pending : 0.008246 seconds
+```
+
+Stop the test server.
+
+```console
+$ docker exec -t $ct openresty -s stop
+```
+
+## Add a test to make sure the test server is offline
+
+```moonscript
+describe "test environment", ->
+ it "can't connect to the internet", ->
+ assert.has_error (-> req "http://example.org"),
+ "Couldn't resolve host name"
+```
+
+## Create a Makefile
+
+Let's create a `Makefile` to make running all these commands easier.
+
+`Makefile`
+
+```Makefile
+image = test-nginx
+loopback = 127.0.0.1
+
+image-build:
+ docker build -t $(image) .
+
+image-rm:
+ docker image rm $(image)
+
+test:
+ @ct=$(shell docker run --rm -d \
+ -v $(PWD)/conf/conf.d:/etc/nginx/conf.d \
+ -v $(PWD)/html:/var/www \
+ -v $(PWD):/opt/app \
+ --network no-internet \
+ --add-host=domain.abc=$(loopback) \
+ $(image)); \
+ docker exec -t $$ct busted; \
+ docker exec $$ct openresty -s stop
+```
+
+Now we can run tests by running `make test`.
+
+```console
+$ make test
+●●
+2 successes / 0 failures / 0 errors / 0 pending : 0.008812 seconds
+```
+
+## SSL
+
+We want our server to redirect all `http` requests to `https`.
+
+Our test:
+
+```moonscript
+describe "http://domain.abc", ->
+ it "redirects to https", ->
+ request = req "http://domain.abc"
+ assert.same request\statusCode!, 301
+ assert.same request\statusMessage!, "Moved Permanently"
+ assert.same request\header!.Location, "https://domain.abc/"
+```
+
+```console
+$ make test
+●●◼
+2 successes / 1 failure / 0 errors / 0 pending : 0.010449 seconds
+
+Failure → .../luajit/lib/luarocks/rocks-5.1/busted/2.2.0-1/bin/busted @ 3
+http://domain.abc redirects to https
+spec/nginx_spec.moon:24: Expected objects to be the same.
+Passed in:
+(number) 301
+Expected:
+(number) 200
+```
+
+Make self-signed certs in Dockerfile:
+
+```Dockerfile
+RUN openssl req -x509 -newkey rsa:4096 -nodes \
+ -keyout /etc/ssl/private/domain.abc.pem \
+ -out /etc/ssl/certs/domain.abc.pem \
+ -sha256 -days 365 -subj '/CN=domain.abc' \
+ -addext "subjectAltName=DNS:domain.abc"
+```
+
+Edit `default.conf`:
+
+```
+server {
+ listen 80;
+ return 301 https://$host$request_uri;
+}
+
+server {
+ listen 443 ssl;
+ server_name domain.abc;
+ ssl_certificate /etc/ssl/certs/domain.abc.pem;
+ ssl_certificate_key /etc/ssl/private/domain.abc.pem;
+```
+
+Rebuild the image:
+
+```console
+$ make image-rm
+$ make image-build
+```
+
+Run tests:
+
+```console
+$ make test
+●◼●
+2 successes / 1 failure / 0 errors / 0 pending : 0.009618 seconds
+
+Failure → .../luajit/lib/luarocks/rocks-5.1/busted/2.2.0-1/bin/busted @ 3
+http://domain.abc sends /index.html
+spec/nginx_spec.moon:17: Expected objects to be the same.
+Passed in:
+(number) 200
+Expected:
+(number) 301
+```
+
+Fix test:
+
+```moonscript
+describe "https://domain.abc", ->
+ it "sends /index.html", ->
+ request = req "https://domain.abc"
+```
+
+Run tests:
+
+```console
+$ make test
+●●●
+3 successes / 0 failures / 0 errors / 0 pending : 0.017065 seconds
+```
+
+## Reverse proxy a subdomain to a Gitea unix socket
+
+Add to `default.conf`:
+
+```
+server {
+ listen 443 ssl;
+ server_name git.domain.abc;
+
+ location / {
+ client_max_body_size 1024M;
+ proxy_pass http://unix:/run/gitea/gitea.socket;
+ proxy_set_header Connection $http_connection;
+ proxy_set_header Upgrade $http_upgrade;
+ 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;
+ }
+}
+```
+
+Add subdomain to certs in Dockerfile:
+
+```
+ -addext "subjectAltName=DNS:domain.abc,DNS:git.domain.abc"
+```
+
+Add a test socket server:
+
+`spec/unixstreamsrvr.moon`
+
+```moonscript
+-- modified from
+-- https://github.com/lunarmodules/luasocket/blob/4844a48fbf76b0400fd7b7e4d15d244484019df1/test/unixstreamsrvr.lua
+socket = require "socket"
+socket.unix = require "socket.unix"
+u = assert socket.unix.stream!
+assert u\bind "/run/gitea/gitea.socket"
+assert u\listen!
+assert u\settimeout 1
+c = assert u\accept!
+
+while true
+ m = assert c\receive!
+ break if m == ""
+ print m
+```
+
+Add a spec:
+
+```moonscript
+describe "https://git.domain.abc", ->
+ it "reverse-proxies request to a gitea unix socket", ->
+ Path = require "sitegen.path"
+ socket_fname = "unixstreamsrvr.moon"
+ socket_dir = "/run/gitea"
+ socket_owner = "nobody"
+ basepath = Path.basepath debug.getinfo(1).short_src
+
+ Path.exec "install", "-o", socket_owner, "-d", socket_dir
+ cmd = "su -s /bin/bash -c 'moon %s' %s"
+ server = io.popen cmd\format Path.join(basepath, socket_fname), socket_owner
+ Path.exec "sleep", "0.1"
+ result = Path.read_exec "find", socket_dir, "-type", "s", "-ls"
+ assert.truthy result\match "nobody%s+nogroup.+" .. Path.join(socket_dir, "gitea.socket")
+
+ req "https://git.domain.abc"
+
+ reqheader = with server\read "*a"
+ server\close!
+
+ assert.truthy reqheader\match "Host: git.domain.abc"
+```
+
+Edit Makefile:
+
+ --add-host=git.domain.abc=$(loopback) \
+
+
+```console
+$ make image-rm image-build
+```
+
+```console
+$ make test
+●●●●
+4 successes / 0 failures / 0 errors / 0 pending : 0.131619 seconds
+```
+
+
+## Conclusion
+
+