--- slug: "site-backend" title: "Site Backend" # date (optional for articles) date: "2024-05-31T03:57:18-04:00" # show on articles page show: true # uncomment to show table of contents toc: true pics: editing: css: "image" tip: "Editing this article with Vim and Firefox." sources: - "/files/articles/site-backend/0-editing-1024.webp" - src: "/files/articles/site-backend/0-editing-1024.png" width: 1024 height: 640 ssl-labs-report: css: "image" tip: "SSL Labs SSL Test results for this site." sources: - "/files/articles/site-backend/1-ssl-labs-20240530-1024.webp" - src: "/files/articles/site-backend/1-ssl-labs-20240530-1024.png" width: 1024 height: 525 securityheaders-report: css: "image" tip: "Security headers report for this site from securityheaders.com" sources: - "/files/articles/site-backend/2-securityheaders-1024.webp" - src: "/files/articles/site-backend/2-securityheaders-1024.png" width: 1024 height: 320 --- ## Overview Site content is managed as [Markdown][] files stored in a [Git][] repository. The theme is a modified version of [Bulma][]. The site is statically generated by [Hugo][]. I create and edit site content with [Vim][] and preview the results locally with `hugo serve -D ...`. Articles and blog posts are saved as drafts and committed to the [Git][] repository so I can work on them as time permits. [{{< pe-figure "editing" >}}][editing-this-article] ## History From 1998 to 2019 this site was generated by a custom [PHP][] backend, written by me. In 2019 I resurrected the site after a several year hiatus and migrated the site from the custom [PHP][] backend to [Jekyll][]. In 2021 I switched from [Jekyll][] to [Hugo][]. ## Goals The content and layout of this site should be: - Small - Fast - Secure - Accessible - Mobile-friendly These goals are addressed as follows: - Small: [Hugo][] is configured to aggressively pack content and minify assets. Images are compressed and served in multiple formats. [HTTP compression][http compression] is enabled (with caution). See [Hugo Configuration][s-hugo-configuration], [Images][s-images] and [Apache Configuration][s-apache-configuration]. - Fast: Static content can be served quickly. Assets are fingerprinted and browser caching is enabled. See [Hugo Configuration][s-hugo-configuration] and [Apache Configuration][s-apache-configuration]. - Secure: Static content; no web-accessible endpoint which can make changes. Additional security measures are discussed in [Deployment][s-deployment], [Apache Configuration][s-apache-configuration], and [Other][s-other]. - Accessible: Addressed when creating content and with custom [shortcodes][shortcode]. See [HTML][s-html] and [Hugo Configuration][s-hugo-configuration]. - Mobile-friendly: Addressed when creating content and with the site theme. See [HTML][s-html] and [Bulma Configuration][s-bulma-configuration]. ## Content This section describes how content is created for this site. ### HTML The site content and layout is designed to be accessible and mobile-friendly: - links and table components have `title` and `aria-label` attributes - images have captions, fallback formats, and `title` and `alt` attributes - images scale gracefully to thumbnails on mobile - the site supports [dark mode][], checks the [color scheme preference][prefers-color-scheme] to determine the default theme, and has a [manual theme switcher][post-theme-switcher] - The menu bar collapes to a hamburger menu on mobile. Here are a few articles which cover guidelines that I follow: - [Progressive Enhancement][] - [Why your website should be under 14kB in size][14kb] - [5 things you don't need JavaScript for][you-dont-need-js] ### Images Images are created as follows: - Mathematical content is created using [Mathy][], saved as [SVG][], minified using [minify][], and served cached and compressed by [Apache][]. - Charts are created with [Matplotlib][], saved as [SVG][], minified with [minify][], and served cached and compressed by [Apache][] Example: [This Python script][bench-chart] was used to generate the bar charts in [this blog post][post-fips203ipd]. - Lossless raster images (e.g. screenshots) are scaled and cropped with [GraphicsMagick][] and served as a lossless [WebP][] with a [PNG][] fallback compressed by [pngquant][]. - Lossy raster images (e.g. pictures) are scaled and cropped with [GraphicsMagick][] and served as a lossy [WebP][] with a [JPEG][] fallback. Other notes: - The animated site logo is an [SVG][] generated by [this Ruby script][gen-logo.rb]. - Menubar icons are borrowed from [Bootstrap Icons][]. - I reviewed several [PNG][] compressors in [this post][post-png-compressors]. Managing images manually helps to keep the site small and fast. Here are a couple common [GraphicsMagick][] commands: ```sh # scale with antialiasing and convert from png to webp gm convert -antialias -scale 1024x1024 some-image.{png,webp} # get image dimensions gm identify some-image.png # convert from png to webp (lossless) gm convert -quality 100 -define webp:lossless=true some-image.{png,webp} # crop image to 256x256 gm convert -crop 256x256 some-image{,-cropped}.png ``` ### JavaScript This site has almost no [JavaScript][], by design. The [JavaScript][] that is used is minimal, [modern][es2015], [deferred][], and only used for the [theme switcher][post-theme-switcher] and burger menu. Below is the unminified `script.js` for this site with some notes removed. It is served as 777 bytes minified and 586 bytes minified and compressed: ```js 'use strict'; // // script.js - script which handles: // // - set theme // - theme switcher and burger menu event handlers // const D = document, C = D.body.parentElement.classList, L = localStorage, M = window.matchMedia, on = (el, id, fn) => el.addEventListener(id, fn); // use theme if set, otherwise fall back to browser preference if (L && L.theme && L.theme === 'dark') { C.add('dark'); // theme set to "dark" } else if ((!L || !L.theme) && M && M('(prefers-color-scheme: dark)').matches) { C.add('dark'); // prefers dark color scheme } document.addEventListener('DOMContentLoaded', () => { // theme toggle event handler on(D.querySelector('.navbar-item[data-id="theme"]'), 'click', (e) => { e.preventDefault(); // stop event L.theme = C.toggle('dark') ? 'dark' : 'light'; // toggle }); // iterate through burgers, bind to click events D.querySelectorAll('.navbar-burger').forEach(e => on(e, 'click', () => { // then toggle is-active on burger and menu [e, D.getElementById(e.dataset.target)].forEach( e => e.classList.toggle('is-active') ) })); }); ``` [Download][script.js] ## Deployment Once I am ready to apply any outstanding changes to the public web site, I push the outstanding commits to a remote [Git][] repository. This triggers a [`post-receive` Git hook][post-receive], which sends an [HMAC][]-authenticated `POST` request to a [web hook][] endpoint on the web server. The [web hook][] verifies the [HMAC][] and then runs [a deployment script][deploy.rb] which does the following: 1. Verifies the authenticated timestamp (to prevent [replay attacks][]). 2. Clones the upstream repository. 3. Executes [Hugo][] (`hugo --minify -d ...`) to build the site in an isolated output directory. 4. Updates the `htdocs` symlink for the public-facing web site to point at the output directory from the previous step. 5. Removes any stale builds. ## Configuration This section discusses the configuration for [Apache][], [Bulma][], [Hugo][], and [Webhook][]. ### Apache Configuration This section disusses the [Apache][] configuration for this site. The information is divided into several sub-sections in order to make it easier to digest. This site relies on the following [Apache][] modules: - [mod\_deflate][mod-deflate]: Enable [HTTP compression][]. - [mod\_http2][mod-http2]: Enable [HTTP/2][]. - [mod\_macro][mod-macro]: Simplify common configuration. - [mod\_proxy][mod-proxy]: Proxy [web hook][] requests to internal [webhook][] daemon. - [mod\_rewrite][mod-rewrite]: Unconditionally redirect from [HTTP][] to [HTTPS][], strip `www.` from the path hostname, and redirect from legacy [URLs][url]. #### Virtual Host Configuration The [Apache][] virtual host configuration is modified as follows: - Unconditionally redirect from [HTTP][] to [HTTPS][] - Unconditionally redirect to strip the `www.` hostname prefix - Enable [HTTP/2][] - Set security headers (discussed in [Security Headers][s-security-headers]) - Enable aggressive caching of image, script, and style assets Below is the [Apache][] virtual host configuration for this site with extraneous comments and configuration for logging, [TLS][], and legacy redirects removed: ```apache # unconditionally redirect to https://pablotron.org <VirtualHost *:80> RewriteEngine On RewriteRule ^/(.*)$ https://pablotron.org/$1 [R,L] </VirtualHost> <VirtualHost *:443> # strip "www." prefix and enable mod_deflate Use STRIP_WWW https://pablotron.org Use MOD_DEFLATE # enable http2 Protocols h2 http/1.1 # set restrictive content security policy Header append "Content-Security-Policy" "default-src 'self'; img-src 'self' https://pmdn.org" # set remaining security headers Header append "Strict-Transport-Security" "max-age=31536000" Header append "X-Frame-Options" "SAMEORIGIN" Header append "X-Content-Type-Options" "nosniff" Header append "Cross-Origin-Opener-Policy" "same-origin" Header append "Cross-Origin-Resource-Policy" "same-origin" Header append "Access-Control-Allow-Origin" "https://pablotron.org" Header append "Referrer-Policy" "strict-origin-when-cross-origin" # set permissions policy Header append "Permissions-Policy" "camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=()" # POST needed for /hooks Header append "Access-Control-Allow-Methods" "POST, GET, HEAD, OPTIONS" # cache images, stylesheets, and javascript for 1 year <FilesMatch "\.(ico|jpg|jpeg|png|gif|webp|svg|js|json|css)$"> Header set Cache-Control "max-age=31536000, public" </FilesMatch> # allow style-src-attr unsafe-inline for svgs # (without this svgs do not render in firefox) <FilesMatch "\.svg$"> Header set "Content-Security-Policy" "default-src 'self'; img-src 'self'; style-src-attr 'self' 'unsafe-inline'" </FilesMatch> # expose webhook <Location /hooks/> ProxyPass "http://localhost:9000/" ProxyPassReverse "http://localhost:9000/" </Location> </VirtualHost> ``` [Download][apache-vhost.conf] #### HTTP Compression [HTTP compression][] is supported via [mod\_deflate][mod-deflate]. It is safe for this site to enable [mod\_deflate][mod-deflate] because it does not use [cookies][] and is not vulnerable to [BREACH][]. In 2022 I tried [mod\_brotli][mod-brotli], but the improvement over [mod\_deflate][mod-deflate] was minimal (deflate: 125k, brotli: 117k) so I abandoned it. #### Security Headers This site uses a strict [Content-Security-Policy][] header; it rejects links to all external assets with one exception: references to images hosted on <https://pmdn.org/> are allowed. The trickiest part of restricting [Content-Security-Policy][] was `style-src`; many content generation tools (including the [Markdown][] table generator in [Hugo][]) break without `style-src 'unsafe-inline'` (e.g., the ability to emit arbitrary `style` attributes). In order to work around the `style-src` issues I ended up [reconfiguring the Hugo syntax highlighter](#hugo-configuration) and writing [a custom Hugo table shortcode][hugo-shortcode-table] which only emits `class` attributes for inline styling. The journey to a strict [Content-Security-Policy][] is documented in this series of blog posts from October 2021: - [Hugo/Content-Security-Policy Impedance Mismatch (October 19, 2021)][post-headers-1] - [TLS and Header Fixes (October 21, 2021)][post-headers-2] - [The Nuclear Option (No More unsafe-inline) (October 25, 2021)][post-headers-3] The remaining security headers are explained in the following articles: - [Security headers quick reference][security-headers-quick-ref] - [Goodby Feature Policy and hello Permissions Policy!][permissions-policy] - [Permissions Policy Explainer][] - [Referrer-Policy][] The security headers on this site earn an A+ rating from [securityheaders.com][]: [{{< pe-figure "securityheaders-report" >}}][securityheaders-report] #### TLS Configuration The certificate for this site is issued by [Let's Encrypt][] and managed automatically by [certbot][] with the [certbot-dns-linode][] plugin. The [Apache][] [TLS][] configuration is based on the intermediate settings from the [Mozilla SSL Configuration Generator][ssl-config]. In particular, only [TLS 1.2][] and [TLS 1.3][] are enabled, and the insecure ciphers from [TLS 1.2][] have been disabled. ```apache # explicit list of cipher suites (from ssl-config.mozilla.org) SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 # use server priorities for cipher algorithm choice SSLHonorCipherOrder on # protocols to enable (only TLS 1.2 and TLS 1.3) SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 ``` [Download][apache-tls.conf] This [TLS][] configuration earns an A+ result from the [SSL Labs SSL Test][ssl-labs-ssl-test]: [{{< pe-figure "ssl-labs-report" >}}][ssl-labs-report] ### Bulma Configuration The site [CSS][] based on the [Bulma][] with the following modifications: 1. All unused components removed 2. monokai style for [Chroma][] added 3. Styles for navbar icon highlighting and table captions added 4. dark mode styles added Here is the [SASS][]: ```sass // style.sass: based on bulma-0.9.3/sass/bulma.sass with the following // changes: // // 1. all unused components removed // 2. monokai style for chroma added // 3. styles for navbar icon highlighting and table captions added // 4. dark mode styles added @charset "utf-8" // import chroma style // // generated with the following command: // // cd themes/hugo-pt2021/assets // hugo gen chromaclasses --style=monokai > chroma.css // @import "chroma" @import "bulma-0.9.3/sass/utilities/_all" @import "bulma-0.9.3/sass/base/_all" // elements @import "bulma-0.9.3/sass/elements/button" @import "bulma-0.9.3/sass/elements/container" @import "bulma-0.9.3/sass/elements/content" @import "bulma-0.9.3/sass/elements/image" @import "bulma-0.9.3/sass/elements/table" @import "bulma-0.9.3/sass/elements/title" @import "bulma-0.9.3/sass/elements/other" // components @import "bulma-0.9.3/sass/components/media" @import "bulma-0.9.3/sass/components/navbar" // grid (reenabled, used for images) @import "bulma-0.9.3/sass/grid/_all" // helpers @import "bulma-0.9.3/sass/helpers/_all" // layout @import "bulma-0.9.3/sass/layout/section" @import "bulma-0.9.3/sass/layout/footer" // dim navbar icons by default .navbar-item .menu-icon opacity: 60% // highlight icons on hover .navbar-item:hover .menu-icon opacity: 100% // table captions below tables table.table caption-side: bottom // dark mode (2024-05-27) @import "dark" ``` [Download][style.sass.txt] With these changes the generated [CSS][] is 137 kB minified and 16 kB minified and compressed. ### Hugo Configuration [Hugo][] is configured to use [Chroma syntax higlighting][chroma] with inline styles disabled in order to support the strict [Content-Security-Policy][]. See [Security Headers][s-security-headers] for details. Tables are are generated by [a custom table shortcode][hugo-shortcode-table], because the [Hugo's][hugo] native [Markdown][] table generator in [Hugo][] uses inline styles. I have also written a couple of custom [shortcodes][shortcode] to generate `<picture>` and `<figure>` elements in order to support [progressive enhancement][]. Custom archetypes have been added for the [Archives][] section, blog posts, articles, and projects. The generated [HTML][] has been modified to: - Use a custom theme. See [Bulma Configuration][s-bulma-configuration]. - Add support for [`<meta name='go-import' ...>`][go-import]. - Add a [Mastodon][] `<link rel='me' ...>` tag. - Remove all unnecessary tags. - Combine, [minify][], and enable caching of [JavaScript][] and [CSS][] assets. - add `integrity` attributes to `<link>` and `<script>` tags. ### Webhook Configuration [webhook][] configuration with [HMAC][] key removed: ```json [{ "id": "deploy-pablotron-org", "execute-command": "/data/www/pablotron.org/git/bin/hook/deploy.rb", "pass-arguments-to-command": [{ "source": "payload", "name": "time" }], "pass-environment-to-command": [{ "source": "string", "envname": "DEPLOY_HTDOCS_PATH", "name": "/data/www/pablotron.org/builds/current" }, { "source": "string", "envname": "DEPLOY_REPO_DIR", "name": "/data/www/pablotron.org/git" }, { "source": "string", "envname": "DEPLOY_BUILDS_DIR", "name": "/data/www/pablotron.org/builds" }], "trigger-rule": { "match": { "type": "payload-hmac-sha256", "secret": "(omitted)", "parameter": { "source": "header", "name": "X-Hub-Signature" } } } }] ``` [Download][webhook.conf] ## Validation I periodically use the following tools to verify this site: - Developer Console: Check page load time, cached and uncached page size. - [Lighthouse][]: Check accessibility, desktop score, and mobile score. - [Security Headers][securityheaders.com]: Check [HTTP][] security headers. - [SSL Labs SSL Test][ssl-labs-ssl-test]: Check [TLS][] configuration. I also manually check the site in the desktop and mobile versions of [Chrome][] and [Firefox][]. I am investigated doing automated validation with [htmltest][], [htmltidy][], and the [W3C validator][], but have not added them yet. ## Other The [robots.txt][] for this site is from [here][robotstxt-ai] and excludes all known [LLM][] crawlers. I do not store credentials (e.g., the [HMAC][] key for the deployment [web hook][]) in the [Git repository for this site][git-repo]. Write access to the [Git repository for this site][git-repo] is only accessible via [SSH][]. [SSH][] access firewalled off and is only available via [Wireguard][]. Here are recommendations regarding [SSH][], in order of preference: 1. Only allow [SSH][] access via a [VPN][] and do not expose it to the public. 2. Only allow key-based authentication and disable password authentication. Use an [Ed25519][] key, if possible. 3. Limit the [IP addresses][ip] which can connect to [SSH][] at the firewall. 4. Always disable remote `root` logins. Note that these options are not mutually exclusive. I have written about firewall configuration and [Wireguard][] elsewhere on this site: - [NFTables Examples][] - [Wireguard is Awesome][] ## Updates This section contains a list of updates to this article it was initially published. - 2024-06-03: [Apache Configuration][s-apache-configuration]: Make [SVGs][svg] render properly in [Firefox][] by adding a `<FileMatch>` directive for [SVGs][svg] which relaxes [Content-Security-Policy][] by allowing `style-src-attr 'unsafe-inline'`. Without this exception inline [SVGs][svg] work fine, but viewing them directly just shows a black box. - 2025-01-30: Added [robots.txt][] info to [Other](#other). [s-hugo-configuration]: #hugo-configuration "Hugo Configuration" [s-security-headers]: #security-headers "Security Headers" [s-images]: #images "Images" [s-deployment]: #deployment "Deployment" [s-html]: #html "HTML" [s-bulma-configuration]: #bulma-configuration "Bulma Configuration" [s-apache-configuration]: #apache-configuration "Apache Configuration" [s-other]: #other "Other" [hugo]: https://gohugo.io/ "Hugo static site generator." [bulma]: https://bulma.io/ "Bulma minimal CSS framework." [mathy]: https://mathy.pmdn.org/ "Online LaTeX editor." [svg]: https://en.wikipedia.org/wiki/SVG "Scalable Vector Graphics vector image format (SVG)" [png]: https://en.wikipedia.org/wiki/PNG "Portable Network Graphics lossless raster image format (PNG)" [webp]: https://en.wikipedia.org/wiki/WebP "WebP raster image format" [vim]: https://www.vim.org/ "vim text editor" [markdown]: https://en.wikipedia.org/wiki/Markdown "Markdown lightweight markup language" [matplotlib]: https://matplotlib.org/ "Python charting library" [pngquant]: https://pngquant.org/ "Command-line tool for \"lossy\" compression of PNG images" [graphicsmagick]: http://www.graphicsmagick.org/ "Command-line swiss army knife tool for image processing" [php]: https://www.php.net/ "PHP programming language" [bootstrap icons]: https://icons.getbootstrap.com/ "Free high quality open source icon library" [jekyll]: https://jekyllrb.com/ "Jekyll static site generator" [git]: https://git-scm.com/ "Free and open source distributed version control" [javascript]: https://en.wikipedia.org/wiki/JavaScript "JavaScript scripting language" [minify]: https://github.com/tdewolff/minify "Minifier Go library and command-line tool" [mastodon]: https://en.wikipedia.org/wiki/Mastodon_(social_network) "Mastodon distributed social network" [css]: https://en.wikipedia.org/wiki/CSS "Cascading Style Sheets" [editing-this-article]: /files/articles/site-backend/0-editing.png "Editing this article with Vim and Firefox" [jpeg]: https://en.wikipedia.org/wiki/JPEG "Joint Photographic Experts Group (JPEG) lossy raster image format" [es2015]: https://en.wikipedia.org/wiki/ECMAScript_version_history#6th_Edition_%E2%80%93_ECMAScript_2015 "Modern JavaScript" [post-theme-switcher]: {{< relref "posts/2024-05-27-site-updates-dark-mode-and-about-pics" >}} "Blog post about dark mode and theme switcher" [prefers-color-scheme]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme "prefers-color-scheme media query" [dark mode]: https://en.wikipedia.org/wiki/Light-on-dark_color_scheme "Dark mode" [webhook]: https://github.com/adnanh/webhook "Lightweight webhook server to run shell commands" [webhook.conf]: /files/articles/site-backend/webhook.conf.txt "Download webhook.conf" [hmac]: https://en.wikipedia.org/wiki/HMAC "Hash-based message authentication code (HMAC)" [apache]: https://apache.org/ "Apache web server" [apache-vhost.conf]: /files/articles/site-backend/pablotron.org.conf.txt "Download Apache virtual host configuration" [apache-tls.conf]: /files/articles/site-backend/tls.conf.txt "Download Apache TLS configuration" [web hook]: https://en.wikipedia.org/wiki/Webhook "HTTP-accessible callback" [deferred]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer "defer attribute of script tag" [replay attacks]: https://en.wikipedia.org/wiki/Replay_attack "Network attack where a valid data transmission is repeated or delayed." [tls]: https://en.wikipedia.org/wiki/Transport_Layer_Security "Transport Layer Security" [post-receive]: https://git-scm.com/docs/githooks#post-receive "Git hook triggered on a remote repository after a push operation" [git-repo]: https://git.pablotron.org/sites/pablotron.org/ "Read-only web view of the Git repository for this site." [deploy.rb]: https://git.pablotron.org/sites/pablotron.org/plain/bin/hook/deploy.rb "Deployment script invoked as a web hook." [gen-logo.rb]: https://git.pablotron.org/sites/pablotron.org/plain/bin/logo-0/gen-logo.rb "Ruby script to generate animated SVG logo." [post-png-compressors]: {{< relref "posts/2023-06-02-end-of-may-miscellany" >}} "Blog post with a review of several PNG compressors" [bench-chart]: https://github.com/pablotron/fips203ipd/tree/main/scripts/bench-chart "Scripts to build and run fips203ipd bench utility with several combinations of backend and compiler, generate a summary CSV, and render a bar chart as an SVG." [post-fips203ipd]: {{< relref "posts/2024-05-15-c11-fips203ipd-v0.6.md" >}} "Blog post about release of fips203ipd v0.6." [script.js]: /files/articles/site-backend/script.js.txt "Unminified JavaScript for this site with some comments removed." [http]: https://en.wikipedia.org/wiki/HTTP "Hypertext Transfer Protocol (HTTP)" [https]: https://en.wikipedia.org/wiki/HTTPS "Hypertext Transfer Protocol Secure (HTTPS)" [http/2]: https://en.wikipedia.org/wiki/HTTP/2 "Hypertext Transfer Protocol, version 2 (HTTP/2)" [post-headers-1]: {{< relref "posts/2021-10-19-hugo-csp-impedance-mismatch.md" >}} "Hugo/Content-Security-Policy Impedance Mismatch - October 19, 2021" [post-headers-2]: {{< relref "posts/2021-10-21-tls-and-header-fixes.md" >}} "TLS and Header Fixes - October 21, 2021" [post-headers-3]: {{< relref "posts/2021-10-25-the-nuclear-option-no-more-unsafe-inline.md" >}} "The Nuclear Option (No More unsafe-inline) - October 25, 2021" [mod-http2]: https://httpd.apache.org/docs/current/mod/mod_http2.html "Apache module which provides HTTP/2 support." [mod-proxy]: https://httpd.apache.org/docs/current/mod/mod_proxy.html "Apache module which provides proxy and reverse proxy support." [mod-rewrite]: https://httpd.apache.org/docs/current/mod/mod_rewrite.html "Apache module which provides rules-based rewriting and redirecting engine." [mod-macro]: https://httpd.apache.org/docs/current/mod/mod_macro.html "Apache module which provides macros within configuration files." [mod-deflate]: https://httpd.apache.org/docs/current/mod/mod_deflate.html "Apache module which provides DEFLATE output compression." [mod-brotli]: https://httpd.apache.org/docs/current/mod/mod_brotli.html "Apache module which provides brotli output compression." [ssl-config]: https://ssl-config.mozilla.org/ "Mozilla SSL Configuration Generator" [tls 1.2]: https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.2 "TLS version 1.2" [tls 1.3]: https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.3 "TLS version 1.3" [ssl-labs-report]: https://www.ssllabs.com/ssltest/analyze.html?d=pablotron.org&hideResults=on "SSL Labs SSL Test results for this site." [ssl-labs-ssl-test]: https://www.ssllabs.com/ssltest/ "SSL Labs SSL Test" [security-headers-quick-ref]: https://web.dev/articles/security-headers "Security headers quick ref" [permissions-policy]: https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/ "Goodbye Feature Policy and hello Permissions Policy!" [permissions policy explainer]: https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md "Permissions Policy Explainer" [referrer-policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin "Documentation for \"Referrer-Policy: strict-origin-when-cross-origin\"" [content-security-policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP "Content-Security-Policy header" [hugo-shortcode-table]: https://github.com/pablotron/hugo-shortcode-table "Powerful Hugo table shortcode which emits HTML using CSS classes rather than inline style attributes." [breach]: https://en.wikipedia.org/wiki/BREACH "Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext (BREACH)" [cookies]: https://en.wikipedia.org/wiki/HTTP_cookie "Small blocks of data created by a web server when a user is browsing a web site." [let's encrypt]: https://letsencrypt.org/ "Free, automated, and open certificate authority (CA)" [certbot]: https://certbot.eff.org/ "Free, open source software tool for automatically using let's Encrypt certificates." [certbot-dns-linode]: https://certbot-dns-linode.readthedocs.io/en/stable/ "Certbot plugin which automates the process of completing an ACME dns-01 challenge by creating TXT records using the Linode API." [securityheaders-report]: https://securityheaders.com/?q=pablotron.org&hide=on&followRedirects=on "Security headers report for this site from securityheaders.com" [securityheaders.com]: https://securityheaders.com/ "Free and easy to use tool to check the web site security headers." [sass]: https://en.wikipedia.org/wiki/Sass_(style_sheet_language) "Syntactically Awesome Style Sheet (SASS)" [style.sass.txt]: /files/articles/site-backend/style.sass.txt "Download SASS configuration" [html]: https://en.wikipedia.org/wiki/HTML "HyperText Markup Language (HTML)" [shortcode]: https://gohugo.io/content-management/shortcodes/ "Snippets in content which call built-in or custom templates." [chroma]: https://github.com/alecthomas/chroma "Chrome syntax highlighter" [archives]: {{< relref "archive.md" >}} "Archived blog posts" [go-import]: https://go.dev/ref/mod#vcs-find "HTML tag which allows Go to map a module name to a version control system repository." [htmltest]: https://github.com/wjdp/htmltest "htmltest static HTML checker" [w3c validator]: https://validator.w3.org/ "W3C HTML validator" [htmltidy]: https://www.html-tidy.org/ "htmltidy" [lighthouse]: https://developer.chrome.com/docs/lighthouse/overview/ "Open source automated tool for improving the quality of web pages." [chrome]: https://www.google.com/chrome/index.html "Google Chrome web browser." [firefox]: https://www.mozilla.org/en-US/firefox/new/ "Mozilla Firefox web browser." [ssh]: https://en.wikipedia.org/wiki/Secure_Shell "Secure Shell" [wireguard]: https://wireguard.com/ "Fast and simple VPN which uses modern cryptography." [vpn]: https://en.wikipedia.org/wiki/Virtual_private_network "Virtual Private Network" [ip]: https://en.wikipedia.org/wiki/IP_address "Internet Protocol (IP) address" [nftables examples]: {{< relref "articles/nftables-examples.md" >}} "NFTables Examples" [wireguard is awesome]: {{< relref "posts/2021-11-06-wireguard-is-awesome.md" >}} "Wireguard is Awesome" [ed25519]: https://en.wikipedia.org/wiki/EdDSA#Ed25519 "Ed25519 digital signature algorithm." [14kb]: https://endtimes.dev/why-your-website-should-be-under-14kb-in-size/ "Why your website should be under 14kB in size" [you-dont-need-js]: https://lexoral.com/blog/you-dont-need-js/ "5 things you don't need JavaScript for" [http compression]: https://en.wikipedia.org/wiki/HTTP_compression "HTTP compression" [url]: https://en.wikipedia.org/wiki/URL "Uniform resource locator (URL)" [progressive enhancement]: https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement "Web design which puts an emphasis on content first." [robots.txt]: https://en.wikipedia.org/wiki/Robots.txt "Robot exclusion protocol." [robotstxt-ai]: https://robotstxt.com/ai "AI / LLM User-Agents: Blocking Guide" [llm]: https://en.wikipedia.org/wiki/Large_language_model "Large Language Model"