If you build WordPress sites for a living, you've probably had this conversation with a client:
"I need the photos behind the members' login. Only paying members should see them."
You set up the membership plugin. The pages are gated. The members are happy. Then someone right-clicks an image, copies the URL, pastes it into a private window, and the image loads. No login. No membership check. Nothing.
So you Google it. And every article on the first page recommends the same three or four plugins, often in the same order. The articles have been recycling the same recommendations since around 2016. The plugins themselves have been doing the same thing the whole time — and most of them won't actually solve your problem.
This post is an attempt to fix that. Not by recommending a different plugin (although I do build one — more on that at the end), but by explaining what each kind of plugin actually does, in plain language, so you can match what you need to what's out there.
The thing nobody tells you up front
WordPress was built for blogs. Blogs have public images. The whole system assumes that anything you upload is something you want the world to see. Your /wp-content/uploads/ folder is, by default, a public folder. The web server hands those files out to anyone who asks, no questions asked.
Your membership plugin doesn't change that. It can hide the page that shows the image. It can't hide the image itself. The image lives at a URL like yoursite.com/wp-content/uploads/2026/05/private-photo.jpg, and that URL works for anyone who has it — member, ex-member, or stranger who got it from someone who screenshot the URL bar.
This is the problem every "media protection" plugin claims to solve. The catch is that there are at least four very different ways to solve it, and the plugins don't tell you which way they use, which means you can't tell whether the one you're installing is going to work for your situation.
The four approaches, in plain English
I'm going to describe these without the technical jargon first, then name names.
Approach 1: Hide the page, not the file
This is what most "content restriction" plugins do. They control who can see a page, who can see a block on a page, who can see a paragraph. They're great at that. But the images embedded in those pages still live at public URLs. If someone has the URL, they can fetch the image, regardless of whether they can see the page that displays it.
This is the approach that catches most designers off guard. You install a plugin called something like "Content Control" or "Restrict Content" and you assume the content is restricted. The text content is. The images are not.
The honest plugins in this category say so up front. Content Control, for example, has 40,000+ active installs and a 4.9-star rating, and right in their plugin description they tell you: Content Control handles media access via content on media attachment pages but won't restrict direct server-level access to media files. That's a fair disclosure. Most of their users don't need direct file protection — they need to gate a paywall page, or hide a block from logged-out visitors, or show different widgets to different roles. For that work, this category is the right tool.
It's the wrong tool if your client said "I need the photos behind the login."
Approach 2: Tell the web server to ask permission first
This is the approach most "prevent file access" plugins use. They write rules that get inserted into a configuration file your web server reads — on most WordPress hosts, this is a file called .htaccess. The rules say something like: "When somebody asks for a file in /wp-content/uploads/, don't just send it. Run it past WordPress first. Ask WordPress whether this person is logged in."
This works. When it works. But it has some real problems your average WordPress designer won't be told about until something breaks:
It depends on what kind of web server your host runs. Most WordPress hosts run Apache, which reads .htaccess files. Some run Nginx, which doesn't. If your host runs Nginx (WP Engine, SiteGround, Kinsta on certain plans, anything "managed performance"), the plugin's auto-generated rules either don't apply at all, or have to be installed manually by your host's support team. The miniOrange plugin's support forum has stories of customers spending weeks getting it working across time zones with their host's tech support. That's not the plugin's fault, but it's also not something the plugin's marketing page tells you.
Every protected file goes through WordPress. Normally, when a browser requests an image, the web server hands it back instantly. With this approach, the web server has to wake up WordPress, load the database connection, check the user's login status, and then decide whether to serve the file. On a busy site with lots of images, that's the difference between a snappy gallery and a slow one.
It only checks "are you logged in?" Not "are you supposed to see this file?" Any logged-in user can fetch any protected URL just by having it. If someone with an active membership shares a link in a Discord server, every other member who clicks it can see it — and so can anyone who joins later, because the membership check happens on the page, not the file.
The plugins in this category are doing what they say. They prevent anonymous access to media files. They don't prevent a logged-in user from sharing URLs with the wrong people, and they don't prevent ex-members from accessing files they bookmarked while their membership was active.
A note on "encrypted URLs" and "secure download links"
Before we get to the next approach, there's something worth calling out, because it's the single biggest reason designers come away from comparing these plugins thinking they're more different than they actually are.
Many Approach 2 plugins advertise features like "encrypted file IDs," "secure download links," or "masked file paths" alongside their actual file protection. These features replace yoursite.com/wp-content/uploads/2026/05/private-photo.jpg with something like yoursite.com/download/AbCd1234.
This is obscurity, not encryption. The URL is shorter and uglier, but anyone who has it can still fetch the file. Nothing about it expires, binds it to a specific visitor, or makes it cryptographically harder to use. If a member shares the short URL in a Discord server, every other member who clicks it gets the file, exactly the way they would with the original path. The protection is still coming from whatever the plugin does at the web server level. The pretty URL is a hat on top of it.
This isn't useless. Obscured URLs do prevent search engines from indexing predictable file paths, and they make it harder for someone to guess at filenames they haven't been shown. Those are real, modest benefits. They are not the same as encryption.
The tell, when you're reading a plugin's marketing page, is whether you can find a clear answer to three questions: what gets encrypted, with what key, and when does the URL stop working? If those answers aren't there, the URL is being obscured rather than secured. If they are there, you're looking at one of the next two approaches.
This matters because it explains why so many of the plugins in Approach 2 look like they're doing different things. Filr advertises "Encrypt File IDs" and "Secure download links." PDA Gold advertises "private file URLs." miniOrange advertises a "private directory." Each has a slightly different cosmetic flavor, but underneath they're all Approach 2 — .htaccess rules and a WordPress permission check — with different paint jobs. The architectural choice you're making is the same regardless of which of those three you install.
The remaining two approaches are different in a way these cosmetic features are not.
Approach 3: Sign the cookie
A smaller group of plugins takes a different approach. Instead of asking the web server to check WordPress for every file request, they put a cryptographic signature in the visitor's browser cookie when they log in or pass age verification. The web server can verify that signature on its own — no database lookup, no PHP, no waking up WordPress. Either the signature is valid and the file gets served, or it isn't and the request gets rejected.
This sounds like a small distinction. It isn't. It means:
- Static file caching plugins still work, because the protection happens at the cookie level rather than inside WordPress.
- The protection works on Nginx hosts as well as Apache hosts, because the verification is happening in a layer above the web server's own configuration.
- A logged-out user can't fake a valid cookie because the signature math is the same math that protects banking sessions.
It still doesn't stop a logged-in user from sharing a working URL with someone else, but the cookie itself expires, and that closes the window on indefinite access.
Approach 4: Bind the URL to the user
The strongest approach, and the rarest in the WordPress plugin market, is to make every file URL unique to the visitor and time-limited. Instead of yoursite.com/wp-content/uploads/2026/05/private-photo.jpg, the visitor sees yoursite.com/protected-media/AbCd1234ExpiringIn15Minutes.jpg. Copy that URL, paste it into a private window, and within fifteen minutes it stops working. Send it to a friend, who might even be a member, and it doesn't work for them at all, because it was generated specifically for the original visitor's session.
This is what banking apps do, what video streaming services do, what every serious DRM system does. It's what your members-only photographer client actually needs, even though they didn't know how to ask for it.
A subcategory worth knowing about: AWS pre-signed URLs
There's one more place Approach 4 shows up in the WordPress ecosystem, and it's worth understanding because designers run into it when they Google "secure WordPress media URLs."
WP Offload Media (now owned by WP Engine) and a handful of similar plugins move your entire media library out of /wp-content/uploads/ and into Amazon S3. Once your files live in S3, you can take advantage of AWS's pre-signed URL feature — short-lived, cryptographically signed URLs that are functionally identical to Approach 4. The math is solid; AWS has been refining it for two decades.
The catch is the migration. To get the protection, you have to:
- Move every existing file off your web server and into S3.
- Set up an AWS account, configure IAM permissions, and manage S3 storage and egress costs.
- Hope that every plugin, theme, and page builder on the site cooperates with the URL rewriting that the plugin does to redirect requests to S3.
- Train your client (or yourself) to upload new media in a way that keeps the new files in S3 instead of accidentally back in
/wp-content/uploads/.
For a site being built fresh by a developer who's comfortable with AWS, this is a fine path. For a designer who's been brought in to add protection to a five-year-old WordPress site with three thousand existing media files, it's a multi-week project that the client is going to wonder why they're paying for. And every page builder and gallery plugin in the install becomes a potential point of failure if its URL handling doesn't play nicely with the offload plugin's rewriting.
This is part of the gap XYZ Protect was built for: Approach 4 without the migration. Files stay where WordPress put them. The cryptographic URL generation and validation happen separately from the WordPress media library, transparently. No AWS account, no IAM policies, no S3 egress bill, no migration to undo if the client ever changes their mind.
A quick map of the major plugins
Here's where the popular plugins fall in those four categories. I'm naming the plugins most likely to come up in your search results so you can recognize them.
Approach 1 — Hide the page, not the file: Content Control, Restrict Content Pro (page features), MemberPress page rules, S2Member's content protection.
Approach 2 — Tell the server to ask permission: miniOrange Prevent files/folders access, Prevent Direct Access (PDA Gold), Filr, MemberPress Downloads (their file protection feature), most plugins with "prevent direct access" in the name.
Approach 3 — Sign the cookie: XYZ Protect's Guard Cookie mode (this is the plugin I build).
Approach 4 — Bind the URL to the user: XYZ Protect's Encrypted URL mode. WP Offload Media (and similar S3-offload plugins) achieves the same kind of protection by moving your library to AWS and using AWS's pre-signed URLs. A handful of enterprise DRM systems do this too, but they're not really WordPress plugins — they're separate platforms with WordPress connectors, and they cost what enterprise software costs.
I'm including my own product because honesty about the landscape requires it. As far as I know, XYZ Protect is the only WordPress plugin that does Approach 4 without requiring you to migrate your media library to a third-party service like S3 — which is the gap that made me build it.
How to figure out which approach you actually need
Here are the questions to ask. If you're a designer who's used to clients asking for things they don't fully understand, this is also a useful conversation to have with them.
Does the client only need the page to be gated, or does the file itself need to be private? If a screenshot of the page is fine — for instance, course content where the videos are on YouTube anyway — Approach 1 is probably enough. If the file is the product, you need Approach 2 at minimum.
How big a deal is URL sharing? If members tend to share URLs in group chats, Discord servers, or among friends, Approach 2 isn't going to be enough by itself. You need Approach 3 or 4.
Does the client run a hosting plan that mentions "Nginx" or "managed performance" anywhere? If yes, Approach 2 is going to be a fight to set up. Approach 3 is more likely to work out of the box.
Does the client use a caching plugin (WP Rocket, W3 Total Cache, similar)? Caching plugins serve cached HTML before WordPress wakes up, which means anything that depends on WordPress checking permissions can break in ways that are hard to debug. Approach 3 plays nicer with caching than Approach 2 does.
Does the client need files to expire — for example, when someone cancels their subscription? Only Approach 4 handles this cleanly. The others all leave a window where bookmarked URLs keep working until the file is moved or renamed.
Why the same plugins keep getting recommended
The Google results have been stuck since 2016 because the SEO has been stuck since 2016. The same articles get rewritten and republished. The same plugins get recommended because they've been around the longest and have the most reviews. The plugins themselves haven't had to change much, because nobody else entered the market with anything different. The whole loop reinforces itself.
This isn't a knock on the older plugins. They do what they do. miniOrange's plugin has 1,000+ active installs and four-star support reviews because they help their customers fight through the Nginx-config-on-managed-host setup process. PDA Gold has been a steady seller for years because the basic move-the-file-and-block-the-folder approach really does keep search engines from indexing your private content. These are real solutions for real problems.
What's missing from the older plugin reviews is the framing that lets you, the designer, tell the difference between "my client's pages are gated" and "my client's files are private," which are two completely different things that get sold under the same heading.
If you've read this far, you can have that conversation with a client and not have it end with a screenshot of an image URL working in a private window six weeks later.
Where I'd start
If a client's content is just text and the occasional image, and the goal is "show different things to different people," start with Content Control or a similar Approach 1 plugin. They're free, they're well-built, and they handle 90% of what most WordPress sites actually need.
If a client's content is the kind of thing where the file itself has to be private — paid photography, member-only videos, downloadable PDFs of paid courses — and they're on a normal Apache host, an Approach 2 plugin like PDA Gold or miniOrange's plugin will probably do the job. Test it on the staging site first; specifically, log out, copy a protected file URL, and try to fetch it.
If your client's content has any of the harder properties — Nginx hosting, an aggressive caching setup, a real concern about URL sharing, files that need to expire when memberships end — that's the situation XYZ Protect was built for. Two modes (Approach 3 and Approach 4), no server configuration, works on any WordPress host, plays nicely with the membership plugins your clients are already using.
Either way: now you know what you're choosing between. That's worth more than another article telling you to install the same five plugins everyone else has been installing for ten years.