I'm trying to override WP's default URL structure for attachments where, if they're attached to a post, the URL is /post-slug/attachment-slug/ and if not it's simply /attachment-slug/.

What I'd like instead is for attachments to behave like posts in that they have an archive and all URLs point to /archive-slug/attachment-slug/.

I've found a new (with 4.4 I believe) filter that allows you to modify post type options but it doesn't seem to work as advertised;

add_filter('register_post_type_args', function ($args, $postType) {
    if ($postType == 'attachment'){
        $args['has_archive'] = true;
        $args['rewrite'] = [
            'slug' => 'media'

    return $args;
}, 10, 2);

If I var_dump($args) they do in fact look correct (has_archive is true etc) but it doesn't appear to have any effect on the URLs at all.

This might not be very surprising if this comment by the devs is correct; "Does not apply to built-in post types" https://core.trac.wordpress.org/changeset/34242.

So my question is how can I still accomplish this?

I've also tried just modifying the post type object in the init hook, but it won't bite:

$obj = get_post_type_object('attachment');

$obj->has_archive = true;
$obj->rewrite = [
    'slug' => 'media'
  • attachment URL output has a filter- attachment_link. you can also add some rewrite rules to handle those /media/attachment-slug/ requests.
    – Milo
    Commented Sep 24, 2016 at 15:45
  • I'd prefer to do it the "real" way if you know what I mean. Trying to fake a new URL feels like it will inevitably fail somewhere.
    – powerbuoy
    Commented Sep 24, 2016 at 15:49
  • 1
    tinkering with the built in post types isn't any more "real" than using the provided filter and API functions, here's some example code from another question.
    – Milo
    Commented Sep 24, 2016 at 15:55
  • By real I mean that post types do have the capability to have archives (using has_archive) not only will this give me the correct URL structure it will also (hopefully) create an actual archive page for the images. So yea, it is more real in that sense. Hacking the rewrite rules for attachments and overriding the permalink functions just feels much more like a hack.
    – powerbuoy
    Commented Sep 24, 2016 at 15:59
  • If I created my own CPT I wouldn't give it an incorrect URL and no archive only to hack one in with rewrite rules later on. I'd prefer to do the same with the attachments.
    – powerbuoy
    Commented Sep 24, 2016 at 16:48

2 Answers 2


This is untested, so apologies there, but I'm thinking out loud, have you tried re-registering the post type afterwards? Or flushed rewrite rules after your initial attempt with flush_rewrite_rules();?

function change_attachment_post_type() {

    $args = get_post_type_object('attachment');
    $args->has_archive = true;
    $args->rewrite = [
        'slug' => 'media'
    register_post_type($args->name, $args);

    // As a temporary one time, remove after first flush
add_action('init', 'change_attachment_post_type', 20);
  • Thanks, I'll give this a try next time, the only thing that makes me doubt it'll work is "Does not apply to built-in post types.".
    – powerbuoy
    Commented May 4, 2017 at 9:03

Here is my solution. First you need to change arguments of attachment post type (as mentioned in the question):

add_filter( 'register_post_type_args', 'change_attachment_post_type_args', 10, 2 );
function change_attachment_post_type_args( $args, $post_type ){
    //Turn on attachment archive page
    if( 'attachment' == $post_type ){
        $args['has_archive']  = 'media';//this is slug
        $args['rewrite']      = true;
    return $args;

Then change the post_status to inherit inside WP_Query because default value of post_status is publish but all attachments have inherit status:

add_action( 'pre_get_posts', 'get_all_posts_attachment' , 10, 1);
function get_all_posts_attachment( $query ) {
    if( is_post_type_archive('attachment') && 'attachment' == $query->get('post_type') )
        $query->set( 'post_status', 'inherit' );
    return $query;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.