Open Closed

Tenant subdomain URL in emails not working #10696


User avatar
0
bmulinari created

Hi there. We have configured ABP’s built-in subdomain tenant resolver using AddDomainTenantResolver(...), and inbound tenant resolution works as expected. Requests to tenant subdomains correctly resolve CurrentTenant.

However, we noticed that Account module emails, like the password reset email sent when a tenant user uses “Forgot password”, do not use the tenant subdomain URL. The generated reset link points to the configured host/base URL, so the link does not work correctly for tenant users in our subdomain setup.

We found that setting the MVC application root URL to the tenant-aware template (replacing it's default value of configuration["App:SelfUrl"]) appears to solve it:

Configure<AppUrlOptions>(options =>
{
    options.Applications["MVC"].RootUrl = "https://{0}.example.com";
});

Can you confirm whether configuring options.Applications["MVC"].RootUrl this way is the correct approach for tenant-aware Account email links? We could not find anything related to this in the documentation, so we want to make sure we are not doing anything we shouldn't that could cause unwanted side effects.

Thanks!

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

5 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • Yes: configuring AppUrlOptions is the correct/expected way to make Account module email links (forgot password / reset password / email confirmation, etc.) point to the correct UI host, including tenant-specific subdomains.
    • ABP’s Account emails build links via the App URL system (AppUrlOptions + IAppUrlProvider). If options.Applications["MVC"].RootUrl (or your relevant UI key) is set to a non-tenant-aware host URL, the generated link will also be non-tenant-aware—exactly what you observed.

    To make links tenant-aware for a subdomain setup, setting a template RootUrl is the right approach, e.g.:

    Configure<AppUrlOptions>(options =>
    {
        // Use the UI application's key that Account uses for link generation in your solution
        options.Applications["MVC"].RootUrl = "https://{0}.example.com";
    });
    

    Notes / side effects (what to expect)

    • {0} is the placeholder ABP uses to inject the current tenant name when generating URLs (similar to how it’s used in the domain/subdomain tenant resolver).
    • This impacts only URLs generated through ABP’s URL system for that application key (including Account emails and other places that call IAppUrlProvider), which is typically what you want in a subdomain-per-tenant setup.
    • If you also need to support host (no-tenant) URLs, ABP will generate host URLs when there is no current tenant (it won’t have a tenant name to substitute). In practice, many solutions handle host separately by using a non-templated RootUrl for host and a templated one for tenant scenarios; but the safe/official mechanism remains configuring AppUrlOptions.

    Sources

    • https://abp.io/docs/latest/framework/architecture/multi-tenancy#domainsubdomain-tenant-resolver
    • https://abp.io/support/questions/7608
    • https://abp.io/docs/10.4/modules/account
    • https://abp.io/docs/10.4/guides/ms-multi-tenant-domain-resolving

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Yes, configuring AppUrlOptions is the correct and supported way to make Account email links (forgot password, email confirmation, change email, user invitation, etc.) tenant-aware. It's not a hack, and there are no hidden side effects. Your config is exactly right:

    Configure<AppUrlOptions>(options =>
    {
        options.Applications["MVC"].RootUrl = "https://{0}.example.com";
    });
    

    A few things worth knowing so you can rely on it with confidence:

    Why the "MVC" key is correct on Blazor Server. In a Blazor Server app, the Account pages that send these emails (login / forgot-password / register) are served by server-side MVC Razor Pages. They submit the request with AppName = "MVC", and the password reset / email confirmation URLs are registered under Applications["MVC"] by default. So Applications["MVC"].RootUrl is exactly the value ABP uses to build those links.

    The {0} placeholder. It's replaced with the current tenant's name. This is the same placeholder semantics used by AddDomainTenantResolver("{0}.example.com"), so as long as your subdomains map to the tenant name (the common case), the outbound email links line up perfectly with your inbound resolution. If your subdomains are based on the tenant id instead, use https://{{tenantId}}.example.com (there's also {{tenantName}} if you prefer to be explicit).

    No side effect for host (no tenant). When there is no current tenant, ABP removes the {0}. part entirely, so https://{0}.example.com becomes https://example.com. Host-side links keep working.

    This single setting covers all Account emails (not just password reset), so you don't need to configure them one by one.

    You're right that this isn't spelled out in the docs. The closest related reference is the subdomain resolver section, which confirms the {0} = tenant name placeholder: https://abp.io/docs/latest/framework/architecture/multi-tenancy#domainsubdomain-tenant-resolver

    And a runnable sample of the subdomain setup: https://github.com/abpframework/abp-samples/tree/master/DomainTenantResolver

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    bmulinari created

    Hi,

    Thanks for the clarification! Maybe this detail of setting Applications["MVC"].RootUrl should be in the documentation, since although not required for the subdomain tenant resolver to work, it's certainly something that should be done for a complete implementation.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Good point, you're absolutely right. We checked the docs and there's actually no dedicated page for AppUrlOptions / IAppUrlProvider at all — not just missing from the multi-tenancy section. It's a framework feature that deserves its own documentation.

    We're going to add a dedicated Application URLs page covering AppUrlOptions (basic config, IAppUrlProvider, multi-tenant placeholders {0} / {{tenantId}} / {{tenantName}}, host fallback behavior, RedirectAllowedUrls), and cross-link it from the Domain/Subdomain Tenant Resolver section so it shows up exactly where you were looking for it.

    I'll follow up here with the PR link once it's opened.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Following up — the docs PR is open: https://github.com/abpframework/abp/pull/25505

    It adds a dedicated Application URLs page covering AppUrlOptions, IAppUrlProvider, multi-tenant placeholders, host fallback, and RedirectAllowedUrls, plus short cross-references from the multi-tenancy, Account, emailing and microservice domain-resolving pages so it's easy to find from where you were looking.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.