Skip to content

Request: Add ability to set a charset on the Content-Type MIME type #3184

Description

@jp-simons

Describe the problem

I think browsers still default to Latin-1, while nearly everybody wants to use utf-8. Out of the box SvelteKit includes <meta charset="utf-8" /> in the app.html but that has a couple issues:

  1. It's just sort of hacky, to tell the parser how to parse while it's parsing.
  2. There is a pitfall I ran into where if you re-arrange things so that meta tags is below the %svelte.head%, it might not be in the first 1024 bytes of the response, which causes Firefox at least to reload the page on each request, making for a double fetch.

Much better to put it in the HTTP response headers.

That line of code is here:

'content-type': 'text/html'

What do you think about an option in svelte.config.js to set this? Happy to submit a PR.

Describe the proposed solution

See above.

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

Activity

  1. Conduitry commented on Jan 2, 2022

    @Conduitry
    Member

    Wanting an HTTP header instead of the meta tag feels niche to me. You should already be able to alter the header in the handle hook.

  2. jp-simons commented on Jan 3, 2022

    @jp-simons
    ContributorAuthor

    That works, doing this conditionally on responses that are HTML looks like this, for reference.

    // hooks.js

    export async function handle({ request, resolve }) {
      let response = await resolve(request)
      if (response.headers['content-type'] == 'text/html') {
        return {
          ...response,
          headers: {
            ...response.headers,
            'Content-Type': 'text/html; charset=utf-8',
          },
        }
      } else {
        return response
      }
    }
  3. jp-simons commented on Jan 22, 2022

    @jp-simons
    ContributorAuthor

    This got quite a bit nicer now with #3384 merged, now it's dealing with UrlSearchParam objects:

    export async function handle({ resolve, event }) {
      let response = await resolve(event)
    
      if (response.headers.get('content-type') == 'text/html') {
        response.headers.set('content-type', 'text/html; charset=utf-8')
      }
    
      return response
    }
  4. rohanrajpal commented on Jul 11, 2024

    @rohanrajpal

    This got quite a bit nicer now with #3384 merged, now it's dealing with UrlSearchParam objects:

    export async function handle({ resolve, event }) {
      let response = await resolve(event)
    
      if (response.headers.get('content-type') == 'text/html') {
        response.headers.set('content-type', 'text/html; charset=utf-8')
      }
    
      return response
    }

    You're a lifesaver, thanks a ton!

  5. Rican7 commented on Mar 19, 2025

    @Rican7

    👋 Here in 2025, I tried to use this workaround, but it caused issues at build time during the prerender step. Issue filed here: #13612

    It can be worked around by adding a conditional to check for building state:

    import { building } from '$app/environment';
    
    export const handle = async ({ event, resolve }) => {
    	const response = await resolve(event);
    
    	// TODO: Remove `building` conditional... It's currently needed for some
    	// reason, as prerendering breaks otherwise...
    	//
    	// See https://github.com/sveltejs/kit/issues/13612
    	if (!building && response.headers.get('content-type') == 'text/html') {
    		response.headers.set('content-type', 'text/html; charset=utf-8');
    	}
    
    	return response;
    };
  6. VityaSchel commented on Dec 2, 2025

    @VityaSchel

    Keep in mind this won't work for static resources.

    Requests for static assets — which includes pages that were already prerendered — are not handled by SvelteKit.

    Source: https://svelte.dev/docs/kit/hooks#Server-hooks-handle

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions