Making a plugin file accessible via url rewrite?

In my WordPress plugin I have a php file that generates some stats about the blog and outputs it as a simple text file. To access WordPress functions I’ve included this at the top of the file:

define('WP_USE_THEMES', false);
require(dirname(__FILE__) . '/../../../wp-blog-header.php');

Now I’d like to hide the actual URL, since it’s in the wp-content/plugins directory and replace it with something nice and short:

Read More
http://example.com/wp-content/plugin/data-fetcher/list-data.php

-> rewrite to http://example.com/list-data

I added this to .htaccess:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteRule ^list-data$ wp-content/plugins/data-fetcher/list-data.php [L]
</IfModule>

The problem: when I access the short URL, I get a 301 Moved Permanently redirect before I end up on the page. This means that the wp-content/plugins/… URL is revealed in the browser.

I suspect that WordPress is initiating this somehow. The rewrite rule works fine for images and plain php files (i.e. without WP includes/require).

Is my assumption correct? How can I prevent WordPress from sending the redirect?

Related posts

Leave a Reply

2 comments

  1. Mark,

    I hate to say it, but you’re going about this the wrong way. PHP files in your plug-in should never be accessed directly in this way. Instead, they should be loaded from within WordPress just like everything else.

    Here are some alternative paths you could take:

    Create an admin page that displays your stats

    This is a page accessible from within WordPress by authenticated users only. If you use the correct API, this page will look very clean and will display your simple stats however you want. Since it’s inside WordPress to begin with, you’ll already have access to the entire WP API without needing to load wp-blog-header.php directly.

    Use your existing system, but register the rewrite with WordPress

    You should never modify your .htaccess file directly. This file can be changed when you add new plug-ins or change your permalink structure, meaning you’ll lose your customizations. Instead, you can create a custom rewrite by making a function call within WordPress itself:

    add_action( 'init', 'my_rewrite' );
    function my_rewrite() {
        global $wp_rewrite;
    
        add_rewrite_rule('list-data/$', WP_PLUGIN_URL . '/data-fetcher/list-data.php', 'top');
        $wp_rewrite->flush_rules(true);  // This should really be done in a plugin activation
    }
    

    This will add your custom rule to the top of the WordPress rewrite rules and should skip any other redirects WordPress matches against the regex.

    For a great example of how you can implement this kind of simple rewriting in a plug-in, check out Ozh’s tutorial on redirecting a pretty login URL:

  2. You can also set a custom variable inside WordPress when your URL is accessed. You can then later check for this variable and if it exists, do whatever you want to do.

    In this example, we will use wpse6891_stats as our variable.

    // Register a URL that will set this variable to true
    add_action( 'init', 'wpse6891_init' );
    function wpse6891_init() {
        add_rewrite_rule( '^stats$', 'index.php?wpse6891_stats=true', 'top' );
    }
    
    // But WordPress has a whitelist of variables it allows, so we must put it on that list
    add_action( 'query_vars', 'wpse6891_query_vars' );
    function wpse6891_query_vars( $query_vars )
    {
        $query_vars[] = 'wpse6891_stats';
        return $query_vars;
    }
    
    // If this is done, we can access it later
    // This example checks very early in the process:
    // if the variable is set, we include our page and stop execution after it
    add_action( 'parse_request', 'wpse6891_parse_request' );
    function wpse6891_parse_request( &$wp )
    {
        if ( array_key_exists( 'wpse6891_stats', $wp->query_vars ) ) {
            include( dirname( __FILE__ ) . '/stats.php' );
            exit();
        }
    }
    

    You can also do the check later, with get_query_var( 'wpse6891_stats' ).