Static files on a subdomain


Is it possible to achieve the following things:

  • Serve generated html files from a top domain (
  • Serve static assets (jpg, css, js, robots.txt), including the assets from page bundles, from a subdomain (
  • Still have relative image links in Markdown with the local hugo server command working as usual
1 Like

I started using Hugo years ago, but have always wondered this exact thing and never asked.

I am also hoping it is possible.

1 Like

I might try the following:

  1. Make your baseURL the top domain (,
  2. For your page images and pages, nothing changes
  3. For your static assets, use a relative permalink and prepend your CDN (e.g. if you are generating your CSS to /public/assets/css, then the .RelPermalink would be /assets/css. If you concatenate to /assets/css you’d get (for each css file, and according to your dir structure, that is).
  4. Use some script to copy or static resources where they belong on the CDN vs everything else that goes on the main server.

I don’t think there is, however, an automated way to do this.

There is a multilingual multihost option, but that is a different beast.

I tried that, but unfortunately it breaks the third scenario I listed - “Still have relative image links in Markdown with the local hugo server command working as usual”.

It is also nice to be able to open a page bundle locally in a markdown preview app (I use Marked 2)

Ah, for that I would wrap the asset link generation in {{ if eq hugo.Environment 'development' }}, e.g. (based on the new theme skeleton’s CSS layout):

  {{- if eq hugo.Environment "development" }}
    <link rel="stylesheet" href="{{ .RelPermalink }}">
  {{- else }}
    {{- with . | minify | fingerprint }}
      <link rel="stylesheet" href="{{ site.Params.cdnURL }}{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
    {{- end }}
  {{- end }}

That would only fix the links in html templates but not in Markdown

For images you could use a render-image render hook. If there are other links you need to modify, you’d need to conditionally modify a render-link render hook.

Markdown render hooks | Hugo (

Also see Joe’s excellent articles:

Thanks! I wasn’t aware that it might be doable using the render hooks! Will try it next week and post an update here.

1 Like

A working solution

baseURL = "/"
cdnURL = ""

Everywhere inside the templates I use the following pattern to link to static assets (images, css, js, favicons):

<link rel="stylesheet" href="{{ .Site.Params.cdnURL }}{{ "css/main.css" | relURL }}">
<img src="{{ .Site.Params.cdnURL }}{{ ($mypage.Resources.GetMatch "*cover.png").RelPermalink }}" alt="Placeholder image">

The image render hook:

{{ $u := urls.Parse .Destination -}}
<img src="{{ if not $u.IsAbs }}{{ site.Params.cdnURL }}{{ if not (hasPrefix $u.Path "/") }}{{ .Page.RelPermalink }}{{ end }}{{ end }}{{ .Destination | safeURL }}" alt="{{ .Text }}"{{ with .Title }} title="{{ . }}"{{ end }}>
{{- /**/ -}}

Unfortunately, hugo deploy does not support exclude/include rules (or custom deploy commands), so I had to switch to rclone:

HUGO_PARAMS_CDNURL="" hugo --gc --cleanDestinationDir
rclone sync public :s3:SITE_BUCKET --s3-provider AWS --s3-access-key-id $AWS_ACCESS_KEY_ID --s3-secret-access-key $AWS_SECRET_ACCESS_KEY --s3-upload-cutoff 1GiB --s3-upload-concurrency 8 --s3-no-check-bucket --filter '+ *.html' --filter '+ /robots.txt' --filter '- **' --delete-excluded --quiet
rclone sync public :s3:ASSETS_BUCKET --s3-provider AWS --s3-access-key-id $AWS_ACCESS_KEY_ID --s3-secret-access-key $AWS_SECRET_ACCESS_KEY --s3-upload-cutoff 1GiB --s3-upload-concurrency 8 --s3-no-check-bucket --filter '- *.html' --filter '- /robots.txt' --delete-excluded --quiet

And hugo server works as expected locally if you do not specify HUGO_PARAMS_CDNURL or set it to empty value.


This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.