state the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    Copyright (C) 2023 Jakub Czajka <jakub@ekhem.eu.org>
+    Copyright (C) 2023-2024 Jakub Czajka <jakub@ekhem.eu.org>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
   If the program does terminal interaction, make it output a short
 notice like this when it starts in an interactive mode:
 
-    website Copyright (C) 2023 Jakub Czajka <jakub@ekhem.eu.org>
+    website Copyright (C) 2023-2024 Jakub Czajka <jakub@ekhem.eu.org>
     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
 
--- /dev/null
+/* Copyright (c) 2024 Jakub Czajka <jakub@ekhem.eu.org>
+   License: GPL-3.0 or later. */
+
+CREATE TABLE IF NOT EXISTS access_logs (
+    access_log TEXT NOT NULL,
+    created_at DATE NOT NULL
+);
+
+DO $$
+BEGIN
+    IF NOT EXISTS (SELECT * FROM pg_user WHERE usename = 'rsyslog')
+    THEN
+      CREATE ROLE rsyslog LOGIN;
+
+      GRANT INSERT
+      ON access_logs
+      TO rsyslog;
+
+      /* Execute for the current database. */
+      EXECUTE FORMAT('GRANT CONNECT
+                      ON DATABASE %I
+                      TO rsyslog', current_database());
+    END IF;
+END$$;
 
--- /dev/null
+/* Copyright (c) 2024 Jakub Czajka <jakub@ekhem.eu.org>
+   License: GPL-3.0 or later. */
+
+DO $$
+BEGIN
+    IF EXISTS (SELECT * FROM pg_user WHERE usename = 'rsyslog')
+    THEN
+        REVOKE INSERT
+        ON access_logs
+        FROM rsyslog;
+
+        EXECUTE
+            FORMAT('REVOKE CONNECT
+                    ON DATABASE %I
+                    FROM rsyslog;', current_database());
+
+        DROP ROLE rsyslog;
+    END IF;
+END$$;
+
+DROP TABLE IF EXISTS access_logs;
 
-# Copyright (c) 2023 Jakub Czajka <jakub@ekhem.eu.org>
+# Copyright (c) 2023-2024 Jakub Czajka <jakub@ekhem.eu.org>
 # License: GPL-3.0 or later.
 
+log_format as_json '{"time": "${dollar}time_iso8601", '
+   '"ip": "${dollar}remote_addr", '
+   '"request": "${dollar}request", '
+   '"path": "${dollar}request_uri", '
+   '"status": "${dollar}status", '
+   '"user_agent": "${dollar}http_user_agent", '
+   '"user_id_got": "${dollar}uid_got", '
+   '"user_id_set": "${dollar}uid_set", '
+   '"remote_user": "${dollar}remote_user", '
+   '"body_bytes_sent": "${dollar}body_bytes_sent", '
+   '"request_time": "${dollar}request_time", '
+   '"http_referrer": "${dollar}http_referer" }';
+
 server {
     server_name www.${public_domain} ${public_domain};
 
     ssl_certificate ${public_ssl_cert_dir}/fullchain.pem;
     ssl_certificate_key ${public_ssl_cert_dir}/privkey.pem;
 
+    userid on;
+    userid_name uid;
+    userid_expires 365d;
+
+    access_log ${website_log_file} as_json;
+
     location /cv {
         root ${prod_dir}/cv;
         rewrite ^ /cv.pdf break;