Skip to content

Conversation

@danielfdickinson
Copy link

@danielfdickinson danielfdickinson commented Aug 9, 2025

Summary

Generates JSON files with headers and redirects information. Optionally generates configuration for Apache 2.4 .htaccess and/or Netlify _headers and _redirects.
This enables setting the Content-Security-Policy with hashes for security. This will allow avoiding the uses of hard-coded nonces[1] and avoids the need to hard code the hashes whenever a script or style changes.

Basic example

Header configuration

  1. Define the headers in data/serverHeaders/static.toml and data/serverHeaders/dynamic/*.toml
    static.toml might look like:

    Strict-Transport-Security = "max-age=31536000; includeSubDomains"
    X-Content-Type-Options = "nosniff"
    X-Frame-Options = "SAMEORIGIN"
    Referrer-Policy = "strict-origin"
    Permissions-Policy = [
      "geolocation=(self)",
      "microphone=()",
      "camera=()"
    ]
    Access-Control-Allow-Origin = "*"
    X-XSS-Protection = "1; mode=block"
    Cache-Control = [
      "public",
      "max-age=31536000"
    ]
  2. a dynamic header might look like this one in data/serverHeaders/dynamic/Content-Security-Policy.toml

    "delimiter" = "; "
    "subDelimiter" = " "
       
    [subFields]
    "default-src" = "'self'"
    "manifest-src" = "'self'"
    
    "connect-src" = [
        "'self'",
        "##VAR_analytics##"
    ]
       
    "font-src" = ""
       
    "img-src" = [
       "'self'",
       "data:"
    ]
       
    "script-src" = [
      "'self'",
      "##VAR_analytics##",
      "'sha256-aWZ3y/RxbBYKHXH0z8+8ljrHG1mSBvyzSfxSMjBSaXk='"
    ]
    
    "style-src" = [
      "'self'",
      "'##VAR_styleHashes##'"
    ]
  3. For the CSP above define the values for ##VAR_analytics## and ##VAR_styleHashes## by creating:

    1. /layouts/_partials/header-custom-values/analytics.html such as:

      {{- with site.Params.analyticsUrl }}{{- . -}}{{- end -}}
    2. /layouts/_partials/header-custom-values/styleHashes.html such as:

      {{- $styleHashes := (union
      ((partial "head/stylesheet-hashes.html" .).hashes)
      ((partial "head/libsass-hash.html" "scss/app.scss").hashes)
      ) -}}
      {{- return $styleHashes -}}          
  4. Enable the header and redirect JSON generation in hugo.toml

    home = ["HTML", "RSS", "searchIndex", "htaccess", "headers", "redirects"]
    
    [outputFormats.headers]
    baseName = "headers"
    isPlainText = true
    mediaType = "application/json"
    notAlternative = true  
    
    [outputFormats.redirects]
    baseName = "redirects"
    isPlainText = true
    mediaType = "application/json"
    notAlternative = true  
    
  5. (Optional) Enable Netlify and/or Apache 2.4 .htaccess configuration generation

    home = ["HTML", "RSS", "searchIndex", "apache_htaccess", "netlify_headers", "netlify_redirects"]
    
    [outputFormats.apache_htaccess]
    baseName = ""
    isPlainText = true
    mediaType = "text/htaccess"
    notAlternative = true
    
    [outputFormats.netlify_headers]
    baseName = "_headers"
    isPlainText = true
    mediaType = "text/netlify"
    notAlternative = true
    
    [outputFormats.netlify_redirects]
    baseName = "_redirects"
    isPlainText = true
    mediaType = "text/netlify"
    notAlternative = true
    
    [mediaTypes]
    
    [mediaTypes."text/htaccess"]
    suffixes = ["htaccess"]
    
    [mediaTypes."text/netlify"]
    delimiter = ""
    suffixes = [""]
  6. Configure domain redirects

    redirectDomains = [
      "wildtechgarden.ca",
      "www.wildtechgarden.com",
      "wildtechgarden.com",
    ]
    targetDomain = "www.wildtechgarden.ca"
    targetScheme = "https"

    NOTE You do not include the main domain (the domain you want to be the baseURL: in this case www.wildtechgarden.ca) in redirectDomains.

  7. Aliases are automatically turned into redirects.

  8. Configure additional redirects. For example, in /data/serverRedirects/path-status.toml

    301 = [
      "/feed /index.xml",
      "/post/feed /index.xml",
      "/develop-design/web-design-web-devel/test-theme-and-debug-tables/hugo-debug-tables-docs/ https://github.com/danielfdickinson/hugo-debug-tables",
      "/devel/hugo-debug-tables-docs/ https://github.com/danielfdickinson/hugo-debug-tables",
      "/projects/old-school/undergraduate/a-study-of-selected-algorithms-against-the-sherlock-and-zebra-problems/definitions-for-zebra-and-sherlock/ /doc/presentations/a-study-of-selected-algorithms-against-the-sherlock-and-zebra-problems/"
    
    ]
  9. Finally, configure any regex redirects in /data/serverRedirects/wildcard.toml (using the same style of config as Netlify).

    301 = [
      "/post/* /blog/:splat",
      "/feed/* /index.xml",
      "/post/feed/* /index.xml"
    ]

Motivation

  1. Enable a proper Content-Security-Policy (and related headers). That is a CSP that with calculated hashes rather than hard-coded nonces and/or 'unsafe-inline' enabled.

  2. Use proper header-based redirects instead of HTTP-REFRESH redirects.

Checks

  • Read Creating a pull request
  • Supports all screen sizes (if relevant) (not relevant)
  • Supports both light and dark mode (if relevant) (not relevant)
  • Passes npm run test (if relevant)

[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP#nonces

@changeset-bot
Copy link

changeset-bot bot commented Aug 9, 2025

⚠️ No Changeset found

Latest commit: 7fad92f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@danielfdickinson danielfdickinson marked this pull request as ready for review August 10, 2025 04:12
danielfdickinson added a commit to danielfdickinson/thulite-doks-core that referenced this pull request Aug 10, 2025
Avoids the need to hard code the hashes when using a CSP. Depends on thuliteio/core#27

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
@danielfdickinson danielfdickinson force-pushed the generate-headers-esp-csp branch from 592db4f to 4b9de12 Compare August 13, 2025 05:52
@h-enk
Copy link
Member

h-enk commented Aug 13, 2025

There are many deployment options, each with its own configuration file. Although Netlify sponsors Thulite’s hosting, I prefer to keep Thulite platform-agnostic and support any hosting provider using its preferred configuration. This approach works well with the current setup — using common practice.

I think automatically generating configuration files can be challenging—for example, when generating them for different routes. I do like the idea of automatically generating hashes for resource files though, and I think that’s something Hugo does well.

This will allow avoiding the uses of hard-coded nonces[1] and avoids the need to hard code the hashes whenever a script or style changes.

Note that you wouldn’t want to add a nonce or hash to an external JavaScript file—if the script changes, it could stop working.

danielfdickinson added a commit to danielfdickinson/thulite-doks-core that referenced this pull request Aug 14, 2025
Avoids the need to hard code the hashes when using a CSP. Depends on thuliteio/core#27

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
@danielfdickinson
Copy link
Author

Note that you wouldn’t want to add a nonce or hash to an external JavaScript file—if the script changes, it could stop working.

Heh. Yeah.

I am pondering the rest of your response..

Collect information needed to generate custom headers, especially
Content-Security-Policy. Information is gathered from Hugo `data`
configuration and generated hashes for stylesheets.

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
And remove the hard-coded hash that is no longer required.

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
Collects headers and redirects from content
frontmatter aliaes and informaton from `data`
entires. With `outputFormat` configuration in
`hugo.toml` allows generating platform agnostic
json with the header and redirect details.

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
With 'outputFormats' and 'outputs' configuration in `hugo.toml`,
adds Netlify `_headers` and/or `_redirects` file generation and/or
Apache 2.4 `.htaccess` configuration with headers and/or redirects.

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
@danielfdickinson danielfdickinson force-pushed the generate-headers-esp-csp branch from 4b9de12 to 75c4f4b Compare August 16, 2025 07:01
@danielfdickinson
Copy link
Author

danielfdickinson commented Aug 16, 2025

I've revamped so that the I can drop the last three commits (sample Apache and Netlify config generation) if you want.

Instead, I genericize the header and redirects collection and emit JSON for the headers and or or redirects (assuming outputs and outputFormats configuration is added to hugo.toml

Does this work for you, so that 'collection' infrastructure is present but the base design is provider agnostic?

For the apache config we need to convert the Netlify style redirect
patterns to Apache regexes and substitutions.

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
danielfdickinson added a commit to danielfdickinson/thulite-doks-core that referenced this pull request Aug 18, 2025
Avoids the need to hard code the hashes when using a CSP. Depends on thuliteio/core#27

Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
@h-enk
Copy link
Member

h-enk commented Aug 26, 2025

I have to give this some proper thought, then I get back to you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants