---
title: "Migrating and accessing public web assets from typo3conf/ext/ to public/_assets"
manual: "TYPO3 Explained"
version: "13.4"
permalink: "https://docs.typo3.org/permalink/t3coreapi:migrate-public-assets@13.4"
source: "Administration/Upgrade/MigrateToComposer/AssetMigration.rst"
rendered: "2026-09-18T05:52:58+00:00"
---

# Migrating and accessing public web assets from `typo3conf/ext/` to `public/_assets` {#migrating-and-accessing-public-web-assets-from-typo3conf-ext-to-public-assets}

TYPO3 v12 requires the Composer plugin
[typo3/cms-composer-installers](https://github.com/TYPO3/CmsComposerInstallers)
with v5, which automatically installs extensions into Composer's `vendor/`
directory, just like any other regular dependency. This increases the default
security, so that files from extensions can no longer be accessed directly via
HTTP.

In order to allow serving assets (images/icons, CSS, JavaScript) from the
public web folder, every directory `Resources/Public/` of any
installed extension is symlinked from their original location to
a directory called `_assets/` within the public web folder (`public/` by
default).

The name of a symlinked directory is created as a MD5 hash to prevent possible
information disclosure. As of now, this hash depends on the extension name
and its Composer project path, so it will not change upon deployment. The specific
hashing is an implementation detail that may be subject to change with future TYPO3
major versions.

For example, a file that was previously accessible as
`public/typo3conf/ext/my_extension/Resources/Public/Images/logo.svg` will
now be stored in `vendor/my-vendor/my-extension/Resources/Public/Images/logo.svg`
and be symlinked to `public/_assets/9e592a1e5eec5752a1be78133e5e1a60/Resources/Public/Images/logo.svg`.

## Migration {#migration}

The general idea is, that if you follow the best practice of referencing to assets
via a `EXT:my_extension/Resources/Public/...` notation where applicable (TypoScript,
Fluid, PHP), you should not need to take further action.

When updating an older TYPO3 installation though, you may want to perform the following
migration steps:

-   Any references from your Fluid templates, CSS/JavaScript files (or similar)
    that pointed to `typo3conf/ext/...` must now be changed (search your extension
    code for `typo3conf/ext/`). Ideally change code within Fluid or TypoScript, so
    that you can use a `EXT:my_extension/Resources/Public/...`
    reference. Those will automatically point to the right `_assets` directory.
    For example, the `f:uri.resource` ViewHelper will help you with this, as
    well as the TypoScript [stdWrap insertData and data path](https://docs.typo3.org/m/typo3/reference-typoscript/13.4/en-us/Functions/Data.html#data-type-gettext-path) or
    [typolink](https://docs.typo3.org/m/typo3/reference-typoscript/13.4/en-us/Functions/Typolink.html#typolink) / [IMG_RESOURCE](https://docs.typo3.org/m/typo3/reference-typoscript/13.4/en-us/ContentObjects/ImgResource/Index.html#cobj-img-resource)
    functionality. Also, in most YAML definitions you can use
    the `EXT:my_extension/Resources/Public/...` notation.
-   Adjust possible frontend build pipelines which previously wrote files into
    `typo3conf/ext/...` so that they are now put into your extension source
    directory (for example, `packages/my-extension/...`).
-   Any other static links to these files (like PHP API endpoints) must be changed
    to either utilize dynamic routes, middleware endpoints or static files/directories
    from custom directories in your project's public web path.
-   References within the same extension should use relative links, for example use
    `background-image: url('../Images/logo.jpg')` instead of
    `background-image: url('/typo3conf/ext/my_extension/Resources/Public/Images/logo.jpg')`.
-   You can use TypoScript/PHP/Fluid as mentioned above to create variables with
    resolved asset URI locations. These variables can utilize the
    `EXT:my_extension/Resources/Public/...` notation, and can be passed along
    to a JavaScript variable or a HTML DOM/data attribute, so it can be further evaluated.
-   If one extension links to an asset from another extension, and you cannot use
    the `EXT:my_extension/Resources/Public/...` syntax (for example, background images
    in a CSS file) you should either:

    -   Create a central, sitepackage-like extension that can take care of delivering
        all assets. CSS classes could be defined that refer to assets, and then other
        extensions could use the CSS class, instead of utilizing
        their own `background-image: url(...)` directives. Ideally, use a bundler
        for your CSS/JavaScript (for example Vite, webpack, grunt/gulp, encore, ...)
        so that you only have a single extension that is responsible for shared assets.
        Bundlers can also help you to have a central asset storage, and distribute
        copies of these assets to all dependencies/sub-packages that depend on these assets.
    -   Utilize a PSR middleware or dynamic routes to "listen" on a specific URL like
        `dynamicAssets/logo.jpg` and create a wrapper that returns specific files,
        resolved via the TYPO3 method
        `PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName('EXT:my-extension/Resources/Public/logo.jpg')`.
    -   If all else fails: You can link to the full MD5 hashed URL, like
        `background-image: url('/_assets/9e592a1e5eec5752a1be78133e5e1a60/Images/logo.jpg')`
        (or create a custom stable symlink, for example within your deployment, that points
        to the hashed directory name).
        The caveat of this: the hashing method may change in future TYPO3 major versions,
        and since the hash is based on a Composer project directory, this is only a suitable
        workaround for custom projects, and not publicly available extensions that need to
        work in all installations. Changes to the location/name of the `vendor/` directory
        would then break frontend functionality.

The [TYPO3-Console](https://packagist.org/packages/helhum/typo3-console) extension has a helpful
`vendor/bin/typo3 frontend:asseturl` command, that lists all the installed TYPO3
extensions plus their public resource directory hash.

For more details and the background about the change, read more:

-   [https://usetypo3.com/composer-changes-for-typo3-v11-and-v12.html](https://usetypo3.com/composer-changes-for-typo3-v11-and-v12.html)
-   [https://b13.com/core-insights/typo3-and-composer-weve-come-a-long-way](https://b13.com/core-insights/typo3-and-composer-weve-come-a-long-way)
-   [https://brotkrueml.dev/migration-typo3-composer-cms-installers-version-4/](https://brotkrueml.dev/migration-typo3-composer-cms-installers-version-4/)
-   [ChangeLog entry #98484](https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.0/Important-98484-ExtensionsOutsideOfDocumentRootForComposer-basedTYPO3Installations.html#important-98484-1664553704)
