{"id":317802,"date":"2026-06-19T17:03:50","date_gmt":"2026-06-19T17:03:50","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/file-security-pro\/"},"modified":"2026-06-19T17:03:14","modified_gmt":"2026-06-19T17:03:14","slug":"folio-gatehouse","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/folio-gatehouse\/","author":13599663,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.8","stable_tag":"1.1.8","tested":"7.0","requires":"5.8","requires_php":"7.4","requires_plugins":null,"header_name":"Folio Gatehouse","header_author":"buffcleb","header_description":"Role-based file access control with zone management, access logging, and .htaccess integrity checking.","assets_banners_color":"4d5854","last_updated":"2026-06-19 17:03:14","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/buffcleb\/folio-gatehouse","header_author_uri":"https:\/\/github.com\/buffcleb","rating":0,"author_block_rating":0,"active_installs":0,"downloads":56,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.1.8":{"tag":"1.1.8","author":"buffcleb","date":"2026-06-19 17:03:14"}},"upgrade_notice":{"1.1.8":"<p>Compatibility fix for WordPress 5.8 (replaces str_starts_with, which requires 5.9). No database or configuration changes.<\/p>","1.1.7":"<p>Public shortcodes renamed to the rbfa_ prefix: [rbfa_files], [rbfa_login_link], [rbfa_zone_link]. A migration rewrites stored zone pages and denial screens automatically; update any of these shortcodes that you typed directly into regular posts or pages.<\/p>","1.1.6":"<p>Renames all plugin-managed roles from fsg_ to fgh_ prefix automatically on first load. DB migration (v1.8) handles all installs including those upgrading from wfsp_ via fsg_.<\/p>","1.1.5":"<p>Renames shortcodes to fgh_ prefix. Existing fsg_ and older shortcode names remain registered as backwards-compatible aliases \u2014 no manual changes needed on existing pages.<\/p>","1.1.4":"<p>Plugin renamed to Folio Gatehouse. Security fixes: nonce added to CSV export, REQUEST_URI sanitized, size_format() output escaped.<\/p>","1.1.3":"<p>Role prefix changed from wfsp_ to fsg_; automatic migration runs on upgrade. Shortcodes renamed \u2014 old names remain as aliases.<\/p>","1.1.2":"<p>Adds export\/import, ZIP download logging, and a fix for <code>[rbfa_zone_link]<\/code> on zone pages. Safe to upgrade \u2014 no database changes.<\/p>","1.1.1":"<p>UI improvements across Roles, Denial Screens, Zones, and Logs tabs plus security hardening. Safe to upgrade \u2014 no database changes.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3579041,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3579041,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3579041,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3579041,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3579041,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.1.8"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"Zones tab \u2014 manage protected directories and assign roles","2":"Logs tab \u2014 filterable, sortable access log with stats widget","3":"Roles tab \u2014 create and manage custom roles with member management","4":"Denial Screens tab \u2014 HTML editor with live sandboxed preview","5":"Settings tab \u2014 system settings, export\/import, and data management"}},"plugin_section":[],"plugin_tags":[1912,267963,267962,1932,246553],"plugin_category":[58],"plugin_contributors":[267964],"plugin_business_model":[],"class_list":["post-317802","plugin","type-plugin","status-publish","hentry","plugin_tags-access-control","plugin_tags-download-protection","plugin_tags-file-protection","plugin_tags-membership","plugin_tags-role-based-access","plugin_category-user-management","plugin_contributors-buffcleb","plugin_committers-buffcleb"],"banners":{"banner":"https:\/\/ps.w.org\/folio-gatehouse\/assets\/banner-772x250.png?rev=3579041","banner_2x":"https:\/\/ps.w.org\/folio-gatehouse\/assets\/banner-1544x500.png?rev=3579041","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/folio-gatehouse\/assets\/icon.svg?rev=3579041","icon":"https:\/\/ps.w.org\/folio-gatehouse\/assets\/icon.svg?rev=3579041","icon_2x":false,"generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>Folio Gatehouse lets you protect files inside your uploads directory by restricting access to specific WordPress user roles. Files are served through PHP \u2014 the web server never delivers them directly \u2014 so direct URL access is blocked regardless of link sharing.<\/p>\n\n<p><strong>Key features:<\/strong><\/p>\n\n<ul>\n<li><strong>Zone-based protection<\/strong> \u2014 define named zones (subfolders inside your uploads directory) and assign allowed roles to each<\/li>\n<li><strong>Custom denial screens<\/strong> \u2014 create HTML pages shown to blocked users, with full control over styling and messaging; separate screens for anonymous and logged-in users<\/li>\n<li><strong>Redirect on denial<\/strong> \u2014 optionally redirect denied users to any URL (e.g. a sales page or membership signup) instead of showing a denial screen<\/li>\n<li><strong>Login redirect shortcode<\/strong> \u2014 <code>[rbfa_login_link]<\/code> inserts a secure login link that returns the user to the originally-requested file after authentication, using an opaque token so no file path is exposed in the URL<\/li>\n<li><strong>Zone virtual pages<\/strong> \u2014 each zone automatically gets a front-end page at <code>\/protected-zone\/{slug}\/<\/code> with customisable title and body content, rendered inside your active theme<\/li>\n<li><strong>Browsable file listing<\/strong> \u2014 <code>[rbfa_files]<\/code> shortcode renders a collapsible, downloadable file listing for authorised users, with per-directory file counts, sizes, and ZIP download buttons<\/li>\n<li><strong>Access logging<\/strong> \u2014 every request is logged with timestamp, username, IP, file path, and status; filterable, sortable, and exportable as CSV<\/li>\n<li><strong>Role management<\/strong> \u2014 create and manage custom WordPress roles (<code>fgh_<\/code> prefix) directly from the plugin, with searchable member management<\/li>\n<li><strong><code>.htaccess<\/code> integrity<\/strong> \u2014 automatically writes and repairs rewrite rules across all protected directories; optional hourly cron<\/li>\n<li><strong>NGINX support<\/strong> \u2014 dedicated tab generates ready-to-copy <code>location<\/code> blocks when NGINX is detected<\/li>\n<li><strong>Export \/ Import<\/strong> \u2014 back up and transfer zones, roles, denial screens, and settings as a JSON file; conflict resolution on import<\/li>\n<\/ul>\n\n<h4>Security<\/h4>\n\n<ul>\n<li>Files served through PHP (<code>readfile<\/code>) \u2014 web server never delivers protected files directly<\/li>\n<li>Path traversal blocked by <code>realpath()<\/code> boundary check before any file is served<\/li>\n<li>Login redirect tokens are opaque \u2014 no file path, role, or zone information in the URL<\/li>\n<li>Denial screen HTML filtered through a strict <code>wp_kses<\/code> allowlist on save and read-back<\/li>\n<li>CSRF protection on every form via WordPress nonces<\/li>\n<li>All <code>ORDER BY<\/code> clauses use a server-side whitelist to prevent SQL injection<\/li>\n<\/ul>\n\n<h4>Requirements<\/h4>\n\n<ul>\n<li>Apache with <code>mod_rewrite<\/code> enabled, <strong>or<\/strong> NGINX (with manual server block configuration \u2014 see the NGINX Config tab)<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>folio-gatehouse<\/code> folder to <code>wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin from <strong>Plugins \u2192 Installed Plugins<\/strong><\/li>\n<li>Navigate to <strong>Folio Gatehouse<\/strong> in the sidebar<\/li>\n<li>Go to <strong>Settings<\/strong> and set your base directory (the folder inside <code>wp-content\/uploads\/<\/code> that will contain all protected zones)<\/li>\n<li>Go to <strong>Zones<\/strong> and add zone rows \u2014 assign a folder slug and the roles that may access it<\/li>\n<li>Click <strong>Save &amp; Sync Zones<\/strong><\/li>\n<\/ol>\n\n<p>If you are running NGINX, visit the <strong>NGINX Config<\/strong> tab for the server block rules you need to add before protection takes effect.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20work%20with%20nginx%3F\"><h3>Does this work with NGINX?<\/h3><\/dt>\n<dd><p>Yes, but you need to add server block rules manually. The plugin detects NGINX and shows a dedicated tab with ready-to-copy <code>location<\/code> blocks.<\/p><\/dd>\n<dt id=\"will%20my%20files%20be%20accessible%20via%20direct%20url%3F\"><h3>Will my files be accessible via direct URL?<\/h3><\/dt>\n<dd><p>Not after <code>.htaccess<\/code> rules are in place (Apache) or after you add the NGINX <code>location<\/code> blocks. All matched requests are routed through WordPress and through the plugin's access check before any file content is returned.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20my%20data%20if%20i%20deactivate%20or%20delete%20the%20plugin%3F\"><h3>What happens to my data if I deactivate or delete the plugin?<\/h3><\/dt>\n<dd><p>Deactivation never deletes any data. Deletion only removes data if you explicitly enable that option in <strong>Settings \u2192 Data Management<\/strong>.<\/p><\/dd>\n<dt id=\"can%20i%20show%20different%20denial%20messages%20to%20guests%20vs%20logged-in%20users%3F\"><h3>Can I show different denial messages to guests vs logged-in users?<\/h3><\/dt>\n<dd><p>Yes. Each zone has separate denial screen dropdowns for anonymous visitors and logged-in users who lack the required role.<\/p><\/dd>\n<dt id=\"can%20i%20use%20this%20to%20protect%20files%20for%20a%20woocommerce%20membership%3F\"><h3>Can I use this to protect files for a WooCommerce membership?<\/h3><\/dt>\n<dd><p>Yes. Create a custom role for your members (or use an existing WooCommerce role), assign it to a zone, and the plugin will enforce access on every file request.<\/p><\/dd>\n<dt id=\"does%20the%20login%20redirect%20shortcode%20work%20with%20custom%20login%20pages%3F\"><h3>Does the login redirect shortcode work with custom login pages?<\/h3><\/dt>\n<dd><p>Yes. Configure the login page URL per denial screen (supports absolute URLs and relative paths like <code>\/my-account<\/code>).<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.8<\/h4>\n\n<ul>\n<li>Replaced two <code>str_starts_with()<\/code> calls with <code>strpos()<\/code> checks for compatibility with the declared minimum WordPress 5.8 (str_starts_with requires WP 5.9)<\/li>\n<\/ul>\n\n<h4>1.1.7<\/h4>\n\n<ul>\n<li>Standardised all public shortcodes on the plugin's 4-character <code>rbfa_<\/code> prefix: <code>[rbfa_files]<\/code>, <code>[rbfa_login_link]<\/code>, <code>[rbfa_zone_link]<\/code> (meets WordPress.org prefix-length guideline)<\/li>\n<li>DB migration (v1.9) rewrites shortcode names in existing zone pages and denial screens automatically on upgrade<\/li>\n<li>Role renames now use core <code>remove_role()<\/code>\/<code>add_role()<\/code> instead of a direct <code>wp_user_roles<\/code> option write<\/li>\n<\/ul>\n\n<h4>1.1.6<\/h4>\n\n<ul>\n<li>All plugin-managed role slugs migrated from fsg_ prefix to fgh_ prefix; DB migration (v1.8) renames existing roles, moves user assignments, and updates zone allowed-roles JSON automatically on upgrade<\/li>\n<li>System role renamed from FSG Admins (fsg_admins) to FGH Admins (fgh_admins)<\/li>\n<\/ul>\n\n<h4>1.1.5<\/h4>\n\n<ul>\n<li>Renamed shortcodes to fgh_ prefix: [fgh_files], [fgh_login_link], [fgh_zone_link]; fsg_ and older names kept as backwards-compatible aliases<\/li>\n<li>DB migration (v1.7) updates existing zone pages and denial screens to new shortcode names<\/li>\n<\/ul>\n\n<h4>1.1.4<\/h4>\n\n<ul>\n<li>Renamed plugin to Folio Gatehouse (slug folio-gatehouse, text domain folio-gatehouse)<\/li>\n<li>Fixed shortcode callback: escape size_format() output with esc_html() in file listing<\/li>\n<li>Added nonce verification to CSV export handler (CSRF protection)<\/li>\n<li>Sanitize $_SERVER['REQUEST_URI'] before use in access log and path derivation<\/li>\n<\/ul>\n\n<h4>1.1.3<\/h4>\n\n<ul>\n<li>Renamed plugin to Folio Gatehouse (slug folio-sentrygate)<\/li>\n<li>Role prefix wfsp_ renamed to fsg_; automatic migration on upgrade<\/li>\n<li>Shortcodes renamed: [fsg_files], [fsg_login_link], [fsg_zone_link]; old names kept as aliases<\/li>\n<li>Fixed inline stylesheet in zone preview iframe<\/li>\n<\/ul>\n\n<h4>1.1.2<\/h4>\n\n<ul>\n<li>Added Export \/ Import feature on the Settings tab \u2014 export zones, roles, denial screens, and settings to a JSON file; import with conflict detection and per-item resolution; role users always merged; only users that exist in the target install are added<\/li>\n<li>[folder_files] ZIP download buttons now recorded in the access log with <code>[zip]<\/code> \/ <code>[zip:all]<\/code> path prefix<\/li>\n<li>Fixed <code>[rbfa_zone_link]<\/code> returning empty when used in a denial screen triggered by a zone page request<\/li>\n<\/ul>\n\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>Roles tab: Create Role moved to modal; role name and member filters; pagination<\/li>\n<li>Denial Screens tab: New\/Edit form moved to modal; label filter; pagination; shortcode reference cards collapsed by default<\/li>\n<li>Zones tab: unmanaged directories in the base folder detected and shown as pre-populated rows<\/li>\n<li>Logs tab: Date From \/ Date To replaced with single datetime-local pickers supporting one-sided ranges<\/li>\n<li>Settings tab: Integrity Repair defaults to checked and persists across zone saves<\/li>\n<li>Security: reject non-<code>\/<\/code> relative redirect paths; <code>sanitize_file_name()<\/code> on Content-Disposition headers; conditional transient delete; ZIP boundary check hardened<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Added FSG Admins system role \u2014 plugin admin access without full administrator<\/li>\n<li>Added multi-user member modal on Roles tab<\/li>\n<li>Added separate anonymous and logged-in denial screens per zone<\/li>\n<li>Added redirect-to-URL option for logged-in users per zone<\/li>\n<li>Added zone virtual pages at <code>\/protected-zone\/{slug}\/<\/code><\/li>\n<li>Added per-zone page editor (split-pane modal with live preview)<\/li>\n<li>Added zone page theme toggle in Settings<\/li>\n<li>Added file count and total size display per zone<\/li>\n<li>Added <code>[folder_files]<\/code> shortcode with collapsible subdirectories and ZIP downloads<\/li>\n<li>Fixed base directory reverting on zone save<\/li>\n<li>Fixed <code>wp_magic_quotes<\/code> slash accumulation in editors<\/li>\n<\/ul>\n\n<h4>1.0.5<\/h4>\n\n<ul>\n<li>Added configurable log pruning \u2014 auto daily cron and manual button<\/li>\n<li>Added NGINX Config tab with generated server block configuration<\/li>\n<li>Added stats dashboard widget with 7-day sparkline<\/li>\n<li>Added <code>X-Robots-Tag: noindex, nofollow<\/code> on all file responses<\/li>\n<li>Added per-zone redirect-to-URL option<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Added Settings tab consolidating system settings and data management<\/li>\n<li>Fixed <code>[rbfa_login_link]<\/code> attribute handling<\/li>\n<li>Extended zones dirty flag to slug, role, and denial screen changes<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Fixed zone saves \u2014 nested form bug silently discarding selections<\/li>\n<li>Moved all POST handlers to <code>admin_init<\/code><\/li>\n<li>Added Post\/Redirect\/Get pattern for all form submissions<\/li>\n<li>Added sandboxed iframe preview for denial screens<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Added <code>[rbfa_login_link]<\/code> with opaque token redirect system<\/li>\n<li>Sortable log columns, date+time filters, configurable pagination<\/li>\n<li>Fixed CSV export headers-already-sent error<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<\/ul>","raw_excerpt":"Role-based file access control. Restrict upload folders to specific user roles, serve files securely through PHP, and log every access attempt.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/317802","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=317802"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/buffcleb"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=317802"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=317802"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=317802"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=317802"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=317802"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=317802"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}