Custom Upload Dir

Description

Ever opened your uploads folder over FTP and wondered which post an image belongs to?

Custom Upload Dir lets you choose a folder structure that fits your site. Keep each post’s images together, group files by category or author, or separate PDFs from photos. Your files become easier to find on the server and easier to work with when making backups or moving your site.

Set up a path template once, then upload through WordPress as usual. Placeholders such as %postname% and %category% are filled in automatically for each new upload. You can combine them with ordinary folder names to create your own structure.

A few useful examples

Suppose you upload sunflowers.jpg to a post called “Summer in the garden”, published in July 2026 and filed under “Gardening”. These templates would put it in:

  • A folder for each post: /%postname%/ /summer-in-the-garden/sunflowers.jpg
  • Year, month, then post: /%year%/%monthnum%/%postname%/ /2026/07/summer-in-the-garden/sunflowers.jpg
  • Category, then post: /%category%/%postname%/ /gardening/summer-in-the-garden/sunflowers.jpg
  • Separate folders for file extensions: /%file_ext%/ /jpg/sunflowers.jpg. A PDF would go in /pdf/.

These paths sit inside your site’s upload folder, usually wp-content/uploads. Post folders use the post’s URL slug, so a title such as “Summer in the garden” becomes summer-in-the-garden.

Make the structure your own

  • Build paths from post slugs, IDs, dates, authors, the person uploading, file extensions and file types.
  • Use categories, tags or your own custom taxonomies as folder names.
  • Keep category hierarchies as subfolders, flatten them into a single folder name, or use just the top-level or most specific categories.
  • Check example paths for your posts on the settings page before uploading files.
  • Upload through the block editor or Media Library as usual.
  • Add your own placeholders with a little PHP, if you need something specific to your site.

Start under Settings Custom Upload Dir. Choose a default template, save it, and use the preview to check example destinations.

Different folders for posts, pages and Media Library uploads

Under Templates by parent post type, you can choose a different destination for uploads to Posts, Pages or a custom post type. The “parent” is the post or page you are uploading to. Each type has three choices:

  • Use default: use the main template at the top of the settings page.
  • Custom template: use a different template for this type. Leave the template empty to store files directly in the upload folder, without subfolders.
  • Leave to WordPress: let WordPress choose the destination using its Media settings and any other upload plugins.

For example, keep Posts on Use default with /%year%/%monthnum%/, and give Pages the custom template /pages/%postname%/. An image uploaded to your “About” page would go in /pages/about/, while post uploads would keep their dated folders.

Uploads without a parent controls files uploaded directly through Media Library Add New. It has the same three choices. For example, /library/%current_user_id%/ keeps these files together in folders for each uploader. Attaching a file to a post later does not move it.

All of these rules start on Use default, so upgrading does not change your destinations. Configure only the rules you want to use.

Folders by author or uploader ID

Use %author_id% for the post author’s numeric ID and %current_user_id% for the person uploading. They can differ when an editor uploads to another author’s post. For example, if user 12 uploads to a post by user 7, /authors/%author_id%/uploads-by/%current_user_id%/ becomes /authors/7/uploads-by/12/.

For Media Library uploads, use %current_user_id%: there is no parent post author, so %author_id% is left out. Uploads without a logged-in user leave out %current_user_id% too.

Choose the post date or the upload date

Use /%year%/%monthnum%/%day%/ for the post’s date, or /%upload_year%/%upload_month%/%upload_day%/ for the date the file is uploaded. Upload dates follow the timezone in your WordPress settings.

For example, an image uploaded on October 13, 2026 to a post dated March 5, 2019 goes in /2019/03/05/ with the post-date template, or /2026/10/13/ with the upload-date template. If there is no dated parent post, both templates use the upload date.

Existing files stay put. The template applies to new uploads; changing it does not move old files or change their URLs. Uploads made directly to the Media Library have no post, so post-specific parts of the template are left out. This organizes the actual files on your server; it does not add virtual folders to the Media Library screen.

Created by Ulf Benjaminsson.

Additional Info

Copyright (C) 2007-2026 Ulf Benjaminsson

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Screenshots

Installation

  1. Extract the custom-upload-dir-folder and transfer it to the /wp-content/plugins/ directory
  2. Activate the plugin through the ‘Plugins’ menu in WordPress
  3. Customize your directory structure in Settings -> Custom Upload Dir

FAQ

Will this add folders to the Media Library?

Custom Upload Dir organizes the actual files on your web server. The WordPress Media Library looks and works as usual. The folder structure is useful when you browse your files through FTP, your hosting file manager or a backup.

Can it reorganize my existing uploads?

No. It chooses the destination for new uploads. Changing the template, renaming a post or changing its categories leaves previously uploaded files and their URLs alone.

Which folders do date and post tokens use?

When uploading to a post or page, %year%, %monthnum% and %day% use its date, even for backdated or scheduled posts. A draft without a date uses the current date in your site’s timezone. The %upload_year%, %upload_month% and %upload_day% tokens always use the upload date in that timezone. %postname% uses the slug; if there is none, it uses the title or, for an unnamed draft, the post ID.

Uploads without a parent use the current site-local time and omit post-specific tokens. Name and save a post before uploading when you want its slug in the folder path. Changing a slug later does not move earlier uploads.

How do I choose year/month folders?

Include /%year%/%monthnum%/ for the post date or /%upload_year%/%upload_month%/ for the upload date. To store files directly in the upload folder, use an empty custom template. Custom templates control the complete path beneath the upload folder; WordPress’s year/month setting does not add extra folders to them.

Choose Leave to WordPress to use its normal behavior instead. With Settings Media Organize my uploads into month- and year-based folders enabled, WordPress uses dated folders; with it disabled, files go directly in the upload folder. Uploads to backdated posts can use the post’s date. Other upload plugins may also affect the destination.

Which tokens and aliases are supported?

The settings page lists the available tokens. You can also use a taxonomy’s name as a token: for example, %product_cat% for product categories. Unrecognized tokens are left out of the path. %file_type% uses the file’s MIME type, such as image/jpeg, creating two folder levels; %file_ext% uses its extension, such as jpg.

Legacy aliases remain available: %mime_type% %file_type%, %post_parent% %parent_name%, %month% %monthnum%, %ID% %post_id%, %post_name% %postname%, and %post_author% %author%. The removed %permalink% token is not supported. The older %post_category% and %post_categories% tokens are retained; prefer %category% for hierarchy settings.

How can I define my own tokens?

Use cud_path_tokens to supply a map of tokens to scalar values, before unresolved tokens are removed. This example adds %site_section%:

function my_cud_tokens( $tokens, $post_id, $file ) {
    $tokens['%site_section%'] = $post_id ? 'posts' : 'library';
    return $tokens;
}
add_filter( 'cud_path_tokens', 'my_cud_tokens', 10, 3 );

Built-in post/file tokens take precedence, then custom tokens, then taxonomy and unknown tokens. Replacement values are literal data and are not recursively expanded. The existing cud_generate_path filter still receives the expanded path and parent ID. Use it to change the final path, not to resolve custom tokens:

function my_cud_final_path( $path, $post_id ) {
    return '/archive' . $path;
}
add_filter( 'cud_generate_path', 'my_cud_final_path', 10, 2 );

For uploads without a parent, the parent argument to these two filters is an empty string. Paths are relative to the upload base, even when written with a leading slash. Drive/URL prefixes, control characters and . or .. directory segments are rejected after filters run. Invalid settings retain the previous saved settings.

What about programmatic imports?

Native uploads and REST media creation carry their request context automatically. A programmatic call to media_handle_sideload() does not expose its parent argument to upload filters. Importers can use the cud_upload_post_id filter to supply that ID for the duration of their upload, then remove their callback in a finally block. Without that context, the upload uses the unattached template behavior. Other upload systems or custom REST endpoints may need their own adapter.

How do I change the base upload folder?

The template creates folders beneath WordPress’s existing upload base. To change that base on a single-site installation, WordPress supports an UPLOADS constant in wp-config.php, relative to the WordPress installation directory:

define( 'UPLOADS', 'wp-content/your-folder' );

Changing the base on an existing site requires a separate migration of files and URLs. This plugin does not perform that migration. Removing the plugin leaves uploaded files in place.

Reviews

September 4, 2023
Большое спасибо из России за качественный плагин.Установил на несколько сайтов.
May 2, 2020 1 reply
Addressed to the author of the plugin : Hello, French, and occasional voluntary developer for associations, I discovered your plugin "custom upload dir" which I found very interesting. Like me, many French people of my generation, (soon to be in their seventies) have limited use of English. So I made a translation of the plugin which, for information, works very well with PHP 5.6.4 and WordPress 5.4 – en_FR You will find attached the plugin with the translation.
March 24, 2019
Does perfectly what it's supposed to do. Used it for years, then it was broken after Gutenberg update, I reported it in the forum and the developer fixed it on the following day. Those options should be included by default in WordPress to really customize it.
Read all 25 reviews

Contributors & Developers

“Custom Upload Dir” is open source software. The following people have contributed to this plugin.

Contributors

Translate “Custom Upload Dir” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Changelog

3.6.0 (2026-10-13)

  • Added separate upload templates for Posts, Pages and custom post types. Each can use the default template, its own template, or WordPress’s normal folders (thanks kristinachilds and pedjas for requesting this!).
  • Added a separate template for uploads made directly through the Media Library (thanks somtam for the feedback on Media Library uploads!).
  • Added %author_id% and %current_user_id% for folders named after the post author’s or uploader’s numeric ID (thanks kryvulena for the request and aldemarcalazans for sharing an early solution!).
  • Added %upload_year%, %upload_month% and %upload_day% for folders based on the upload date in your site’s timezone. Existing date tokens continue to use the post date (thanks jslanglois and aslynn for highlighting the confusion between these dates!).
  • Added path previews and clickable placeholders for the new templates.
  • Updated French, German and Swedish translations and settings help.

Upgrading alone preserves existing destinations. No media is moved.

3.5.0 (2026-10-12)

Features and improvements

  • A clearer settings page with clickable, keyboard-accessible placeholders and simpler explanations.
  • Better path previews, including uploads without a post and feedback when a preview post cannot be found.
  • Developers can now add custom placeholders using cud_path_tokens, fixing the old approach that removed them before they could be replaced. The FAQ includes working examples (thanks jeffpannone and tomeagles!).

Bug fixes

  • Fixed cases where block-editor uploads used the wrong post’s details or lost the post information needed for the folder name.
  • Fixed failed uploads affecting the folder chosen for a later upload.
  • Fixed folder calculations that could accidentally remove part of the site’s upload base path.
  • Fixed settings saves that could discard values. Invalid templates now show an error and keep the previous settings.
  • Fixed the documented %mime_type% placeholder and restored support for older placeholder names.
  • Fixed PHP notices when saved settings are missing taxonomy options (thanks aljuk!).
  • Improved handling of missing authors and deleted categories.

Maintenance

  • Cleaned up older PHP code and made settings-page output safer.
  • Updated the French translation and added German and Swedish translations for the settings page and messages.
  • Reworked the help and examples to make choosing a folder structure easier.
  • Minimum requirements are now WordPress 5.7 and PHP 7.4.

When upgrading: your saved template and existing files are kept. Dated folders now come only from your template: include /%year%/%monthnum%/ if you want them. Saving plugin settings no longer changes WordPress’s separate Media settings.

3.4.6 (2020-07-17)

  • Fixed a null dereference error.

Changelog v. 3.4.5 (2020-05-05)

  1. added french translation (thanks, bibi699depreau!)

Changelog v. 3.4.4 (2019-03-24)

  1. Fixed a few PHP warnings
  2. Removed the %permalink% template as it broke uploads to drafts.

Changelog v. 3.4.3.2 (2019-03-24)

  1. Fixed ‘upload_dir’ not providing $post_id in Gutenberg editor.

Changelog v. 3.4.1 (2015-03-16)

  1. Added filter ‘cud_generate_path’ so you can easily adjust the path with any variable you want.
  2. Added %mime_type% to sort files by mime type
  3. Added %author_role% to sort files by author role.
  4. Coming: support for per-post-type settings. Eg: every post type can have their own rules for where to place uploads. Currently missing the admin interface. If you feel like helping out, let me know.

Changelog v. 3.4 (2013-02-09)

  1. Added %file_ext% to sort by file type
  2. Added tentative support for uploads from WordPress apps.
  3. Removed setting for “Base upload folder” as WP core no longer exposes this
  4. Cleared out some PHP error on the admin page
  5. Fixed leaking CSS on admin page

Changelog v. 3.3.2 (2011-12-29)

  1. Updated init to use WordPress API properly
  2. Removed dead code

Changelog v. 3.3.1 (2011-12-27)

  1. Fixed poor sanitization of post slugs (%post_name%)
  2. Added %post_parent%
  3. Fixed settings not always initializing

Changelog v. 3.3 (2011-12-18)

  1. Support for WordPress 3.3
  2. Support for tags and categories
  3. Support for custom taxonomies
  4. Added %current_user%, %permalink%
  5. Added all permalink tags
  6. Added test-function to admin page

Changelog v. 3.0.2 (2011-07-29)

  1. Fix for post_author not working
  2. Fix timestamps from the media uploader (thanks; alx359)

Changelog v. 3.0.1 (2010-11-01)

  1. Fix for “undefined function get_userdata()” (thanks somatic!)

Changelog v. 3.0 (2010-10-21)

  1. Rewrote entire plugin for WordPress 3.0
  2. A slew of new placeholders for building path’s with
  3. A new interface to allow arbitrary strings in path
  4. Error messages when uploading to un-named or un-dated posts
  5. Added translation support

Changelog v.2.5.4 (2008-12-31)

  1. Adjusted the test case. (Pedja)

Changelog v.2.5.3 (2008-12-30)

More problems have crept up due to the massive amount of dry runs of ‘upload_dir’ in WP 2.7.

  1. Added quick bail for every call that’s not for a file upload. (props to Pedja Ljubomirovic (Canada))

Changelog v.2.5.2 (2008-12-15)

  1. Fixed problem with plugin blocking deletion/editing of attached files.

Changes in v.2.5.1 (2008-12-14)

  1. Fixed Custom Upload Dir breaking WP 2.7 when called from the loop

Changes in v.2.5 (2008-11-15)

  1. Support for WP 2.7-beta1
  2. Added ‘post_author’
  3. Fixed ‘post_title’ (broken since WP 2.4)
  4. Fixed issue with use_yearmonth overriding post date
  5. Tons of small(ish) updates
  6. Removed Mediacaster-support

Changes in v.2.0 (2007-10-19)

  1. Mediacaster compability.
  2. Added proper configuration screen
  3. Massive code overhaul
  4. Name change (previously “Daily Upload Dir”)