A WordPress URL rewrite rule to phase out year/month folders in the Media Library

This is one of those fixes where it is probably worth me explaining the very specific use case I needed it for first, to serve as an example of exactly why anyone would need this, because on the surface it may seem pointless.

Picture this: Restaurant client. Table cards with QR codes linking directly to menu PDFs, so customers can look at the menu on their phones instead of using a physical print menu that has been handled by dozens of other people.

Note to the future: I’m not sure what the restaurant experience looks like in your world. What I’m describing may be ubiquitous for you now, or may be a complete head-scratcher. Assuming it’s safe for you to touch your head. For context, I am writing this in the midst of the 2020 COVID-19 pandemic.

Here’s the problem: Menus change. URLs referenced by a QR code do not. By default, WordPress automatically creates year and month subfolders inside wp-content/uploads and puts files in the folder for the year and month the post they’re attached to was created, or if you’re uploading directly into the Media Library, not attached to a post, then the year and month the file was uploaded.

So that means that the URLs embedded in my client’s QR codes contain 2020/09. But now it’s October, so if they upload a replacement file today, its URL will contain 2020/10 and the QR code will not work. I should note at this point that I do not like the default WordPress behavior of putting files into these subfolders, but I sometimes forget to turn off this setting when I’m creating a new site, or — as is the case here — I’m working on a site someone else originally set up.

My solution: Turn off year/month folders, so that any newly uploaded PDFs with the same filename will have the same URL. (Assuming the client deletes the old one first!)

You may be thinking, well, that’s great, if you had done this before the QR codes were created. Yes, exactly. That’s where this rewrite rule comes in.

When you turn off the year/month folder setting, it doesn’t move any existing files or change any code that links to them. This purely affects new uploads going forward. So what I need is a rewrite rule that will allow existing file URLs with the year/month path to continue working, while automatically removing that bit from the URL and trying to find the same file in the main uploads folder, if there’s no file at the year/month URL.

OK, here’s the code:

# Redirect file URLs from year/month subfolders to base uploads folder if not found
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wp-content/uploads/
RewriteRule ^index\.php$ – [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([0-9]{4})/([0-9]{2})/(.*) /wp-content/uploads/$3 [L,R=301]

This should go not in your main .htaccess file, but in an .htaccess placed inside your wp-content/uploads folder.

Let’s assess what’s going on here, line by line.

# Redirect file URLs from year/month subfolders to base uploads folder if not found

Just a comment so we remember what this is all about. You may think you’ll remember. But you probably won’t. Comments are your future friend.

<IfModule mod_rewrite.c> and </IfModule>

Apache configuration conditionals wrapper for all of our actions, to make sure this code doesn’t run if mod_rewrite isn’t enabled. Honestly I often leave this out because… come on, the entire site is going to be broken if mod_rewrite isn’t enabled.

RewriteEngine On

If you don’t know what this is about, RTFM. (The “F” is directed at the manual, not you. I hate the Apache documentation.)

RewriteBase /wp-content/uploads/

This is the reference point for the ^ later on. Needs to be the relative path of the uploads folder below your WordPress site’s base URL.

RewriteRule ^index\.php$ - [L]

Honestly we probably don’t need this line, as there shouldn’t be any index.php files inside your uploads folder anyway, but it just feels weird not to include it. This just says “don’t do any rewrites to the index.php file.”

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

These lines are very common in this type of rewrite instruction set, and in fact come straight from the default WordPress rewrite rules. They are saying, only apply the following rules to URLs that don’t match existing real files or directories under this path. This is critical to keep all of your existing Media Library URLs working.

RewriteRule ^([0-9]{4})/([0-9]{2})/(.*) /wp-content/uploads/$3 [L,R=301]

This is what we’re here for. Note we are using the magic of regular expressions to make this work. There are three parenthetical groupings, though we technically could eliminate the first two sets of parentheses and change $3 to $1, but I just like having the parentheses to help me keep things straight.

([0-9]{4}) is matching a 4-digit number, representing the year folder.

([0-9]{2}) is matching a 2-digit number, representing the month folder.

And (.*) is matching… anything, representing the filename. That’s the bit we want to reference in the replacement string, /wp-content/uploads/$3 which tells Apache to serve up the filename from the year/month URL directly out of the uploads folder itself.

The final bit, if you’re not familiar with rewrite rules, [L,R=301], just says this is the last rule the previous set of conditions applies to, and that it should return an HTTP 301 (permanent redirect) status along with the redirect, which is good SEO karma.

How to install Windows 8 on a MacBook Air

No longwinded backstory in this post. I’m just posting this here so I can remember it if I ever have to install again, since I seem to keep forgetting.

If you’re trying to install Windows 8 (or Windows 7) on a MacBook Air, and you boot to the Windows CD (from a SuperDrive, of course), you may find that when you try to select the BOOTCAMP partition, you get an error stating that Windows can’t be installed on this drive, because it’s in GPT format, and you need to have an NTFS partition.

Well, it doesn’t matter if you have that partition formatted as NTFS or not. The error is happening because of the way you booted up!

Quit the installer, and restart, holding down the Option key. Then when the disk selection comes up, don’t select the Windows installer, select EFI Boot instead. That’s it!


Yes, from now on all of my blog posts will have one-word titles, ending in an exclamation mark.

But seriously… a couple months back I mentioned a new track I had recorded for the first issue of Ramen Music. That track didn’t make the cut, nor did it make it onto my recently released prog rock album Three, mainly because its style didn’t fit the rest of the album (which is the main reason it didn’t make it onto Ramen Music #01 either, apparently).

I still like it though, and I want you to hear it, so here you go.

[audio:http://blog.room34.com/wp-content/uploads/underdog/Sembei.mp3|titles=Room 34: Sembei]

I have not formerly addressed this formally

Malaprop'sI’m a geek. Let’s get that straight. I’m not a nerd. Geeks are people who tend to be obsessive over a particular set of arcane knowledge. Nerds are people who are obsessive about learning. The differences are subtle, and to some extent overlapping. I could draw you a Venn diagram, but then, simply by virtue of the suggestion, I think I prove my point. SLP is a nerd. She will provide historical citations to back me up on this. In short, nerds read more books than geeks. At least if you exclude sci-fi/fantasy.

Anyway, yes, there places where geekdom and nerdery overlap. One of those places is in language itself. So perhaps I’m being a bit nerdy here, and not just geeky as usual, but I feel I must address one of my longstanding pet peeves of language misuse that I’ve been observing with increasing frequency: the confounding (to me) confusion (by others) over the words formerly and formally. These two words are not even close in meaning, yet I often see one substituted for the other — almost always “formally” for “formerly,” come to think of it. I suspect that many people just don’t even know “formerly” is a word.

Perhaps, formerly, you were one of them. But no longer.

A Google search confirms that I am not just imagining this phenomenon, and the first result, from About.com, elucidates the difference succinctly:

The adverb formally means “in a formal way.” The adverb formerly means “at an earlier time.”

Another way to look at it is to simply drop the “-ly” adverb suffix and compare the base adjectives: “formal” and “former.” So, really, maybe the problem here is that none of you were paying attention in fourth grade grammar class. Clearly, I think that those who are misusing these words are failing to understand some aspect of the linguistic structure underlying them. Because if you do understand, it’s almost impossible to mix them up.

Or is it?

I certainly thought the two words were about as distinct as could be, until I looked at this quiz which challenges you to read a sentence and determine which of these two words belongs in it. It’s not as clear cut as you might think. For example:

I recognize her face, but she and I never have been introduced (formally/formerly).

My gut tells me to use “formally” here, but “formerly” would also technically be correct (even though “previously” — or nothing — would probably sound better). However, all of the questions in this quiz are only challenging from the perspective of the reader/listener, who is trying to determine the original intent of the writer/speaker. The writer/speaker should not be struggling with this ambiguity, assuming they themselves know what they’re trying to say.

So we’re right back at square one: do you know what you’re trying to say, or not? And if not, why are you talking at all? Formerly I would have been more tolerant, but now I am formally asking you to get it right before you write. All right?

The merits of a fresh start

It’s hard to start over. Make a clean break. Go back to the very beginning and build things from the ground up. I experience it all the time in web development. Most of the time, starting over from scratch in this field is seen in a negative light. “Reinventing the wheel” is the standard metaphor. Why do something you’ve (or someone else has) already done all over again? Better to take what you’ve already built once or twice or a hundred times before and just reuse what you can, tweak as needed, give it a fresh coat of paint (so to speak) and call it a day — to keep the clichés coming, fast and furious. Check.

But… on the other hand… did you really do it the best you could on the very first try? Is it another coat of paint on a sturdy, reliable structure that’s stood the test of time, or just another layer of lipstick on the pig? Well, for better or worse that’s what I do, most of the time. It’s what most of us do. Because even if we have the chance, starting over from the beginning means a lot more work, and isn’t what we’ve already got, good enough?

Maybe. But is “good enough” really good enough? Really?

Today my 5-year-old son was bored. BOOOOORED. And he asked me what he could do. I spotted on the shelf above my desk the bulk Legos I had purchased several months ago to keep on my desk at my old job. They’ve pretty much just been sitting in their containers since I’ve been working from home, and I decided it was time to break them out.

So we sat at the table and started building. I had packed them somewhat hastily back when I left that job, and they were still mostly clumped together into the odd, improbable configurations I always liked to build when I was sitting at my desk mulling over a coding problem.

As we began putting the Legos together, I mostly kept these proto-creations and just added on to them or made slight modifications. It seemed too time-consuming and counterproductive to take them all apart and start over again. But then I stepped away for a few minutes, and when I returned, I discovered he had completely disassembled them all. Everything. Nothing but individual pieces, and we had to start over.

I grabbed a few pieces and began to imagine the configuration of whatever it might be that I was about to build. And then the ideas began to flow. I started seeing arrangements I never would have imagined — or, more precisely, bothered to imagine — otherwise. The end result was probably one of the most interesting and (dare I say it) whimsical Lego creations I’ve ever come up with. (And I don’t use the word “whimsical” lightly. Or at all.)

And it never would have happened if I hadn’t gone back to square one and started over.

Thanks, kid. Those of you who are registered users can see more pictures from our Lego project here.