HubSpot CMS for Developers study materials
HubSpot CMS for Developers study resources

Membership Gate: Public Page With a Conditional Download

HubSpot CMS for Developers · Put your knowledge to work in context.

All case studies

Membership Gate: Public Page With a Conditional Download

A professional association has 4,200 members and a HubSpot CMS site. Its resource library has a public listing page at /resources and detail pages at /resources/{slug}. Each detail page contains a 900-word summary and a gated PDF download. The detail template uses {% if request_contact.is_logged_in %} to show the download button and member-only notes, and otherwise shows a 'Member login' link. Pages are public, not private. Members log in through a HubSpot membership login page. Symptoms: some members report seeing the logged-out state after logging in; a few report seeing another member's first name in the header; Google has indexed member-only notes from a detail page; the logout link sometimes redirects to a 404. The team wants to keep the summaries public for SEO but protect the PDF and notes.

1. Assess whether request_contact.is_logged_in is sufficient protection for the PDF and notes. What should change?

Read the worked answer

request_contact.is_logged_in is a rendering condition, not an access-control boundary. If the HubL runs server-side, a logged-out visitor's HTML may omit the notes, but the page itself is still public and the PDF URL may be discoverable through the file manager, search, analytics, or a previously indexed version. Conditional markup also breaks badly when caching serves a logged-in variant to a logged-out visitor or vice versa.

I would separate public SEO content from protected content. Keep the 900-word summary on a public page with its own canonical URL and no member-only notes. Put the PDF and member notes behind a genuinely private page or a membership-protected download route, using HubSpot's page access settings and a contact list rather than a template if. The public page links to a login or access-request page. The private page should not be indexed and should redirect unauthenticated users to login with a return URL. If the file itself cannot be protected by membership, do not expose its direct URL in public HTML; serve it through a controlled route that checks membership before redirecting.

2. Explain the caching and personalization symptoms and how you would fix the login state and name leak.

Read the worked answer

Seeing another member's first name is a classic sign that personalized HTML was cached and served to a different visitor. A public page that renders request_contact data can be cached at the CDN or page level; the cache key may not vary by the logged-in contact. Login-state links can go stale for the same reason, and if the logout URL is generated from a cached variant or points at a page that no longer exists, the 404 is expected.

Fix: do not render contact-specific data in a public, cacheable header. Move personalized greetings and member-only notes to private pages, where access control and caching rules are designed for logged-in users. Avoid using template conditionals to fake privacy on a public page. Verify cache behavior by logging in as two different test contacts in the same browser profile and by checking response headers for public versus private pages. For the logout 404, use the membership logout URL or the correct page path, and test it from both the private and public pages.

3. Design the access model: what is public, what is private, and how should redirects work?

Read the worked answer

Public: the /resources listing and each summary page. These should be indexable, canonical, and free of member data. They can show the PDF title and a login CTA, but not the file URL or member-only notes. Private: a download page or member resource page that requires membership and is tied to the correct contact list. That page can show the PDF link and notes, and should redirect unauthenticated visitors to the membership login page with a return URL. The login page itself should be public and should not be cached with a stale redirect target.

Use page-level membership settings for the private page, not a HubL if on a public page. Keep the public summary's canonical pointing to itself, and make sure the private page is not indexed. For the listing, link to the public summary; the summary's download CTA goes to the private page. Fix the logout link to the correct membership logout route and test the full loop: logged out -> summary -> login -> private page -> logout -> summary. That loop is where the 404 and stale-state symptoms will surface.