diff --git a/package.json b/package.json
index 68d081c40e247..0249bd3d1efd4 100644
--- a/package.json
+++ b/package.json
@@ -3668,7 +3668,8 @@
 										"fileInCommit",
 										"fileInBranch",
 										"fileLine",
-										"fileRange"
+										"fileRange",
+										"tag"
 									],
 									"properties": {
 										"repository": {
@@ -3706,6 +3707,10 @@
 										"fileRange": {
 											"type": "string",
 											"markdownDescription": "Specifies the format of a range in a file URL for the custom remote service\n\nAvailable tokens\\\n`${start}` — starting line\\\n`${end}` — ending line"
+										},
+										"tag": {
+											"type": "string",
+											"markdownDescription": "Specifies the format of a tag URL for the custom remote service\n\nAvailable tokens\\\n`${repo}` — repository path\\\n`${tagName}` — name of the tag"
 										}
 									},
 									"additionalProperties": false
@@ -6757,6 +6762,22 @@
 				"title": "Open Commits on Remote",
 				"icon": "$(globe)"
 			},
+			{
+				"command": "gitlens.openTagOnRemote",
+				"title": "Open Tag on Remote",
+				"category": "GitLens",
+				"icon": "$(globe)"
+			},
+			{
+				"command": "gitlens.views.openTagOnRemote",
+				"title": "Open Tag on Remote",
+				"icon": "$(globe)"
+			},
+			{
+				"command": "gitlens.views.openTagOnRemote.multi",
+				"title": "Open Tags on Remote",
+				"icon": "$(globe)"
+			},
 			{
 				"command": "gitlens.copyRemoteCommitUrl",
 				"title": "Copy Remote Commit URL",
@@ -6773,6 +6794,22 @@
 				"title": "Copy Remote Commit URLs",
 				"icon": "$(copy)"
 			},
+			{
+				"command": "gitlens.copyRemoteTagUrl",
+				"title": "Copy Remote Tag URL",
+				"category": "GitLens",
+				"icon": "$(copy)"
+			},
+			{
+				"command": "gitlens.views.copyRemoteTagUrl",
+				"title": "Copy Remote Tag URL",
+				"icon": "$(copy)"
+			},
+			{
+				"command": "gitlens.views.copyRemoteTagUrl.multi",
+				"title": "Copy Remote Tag URLs",
+				"icon": "$(copy)"
+			},
 			{
 				"command": "gitlens.openComparisonOnRemote",
 				"title": "Open Comparison on Remote",
@@ -10560,6 +10597,18 @@
 					"command": "gitlens.views.openCommitOnRemote.multi",
 					"when": "false"
 				},
+				{
+					"command": "gitlens.openTagOnRemote",
+					"when": "gitlens:repos:withRemotes"
+				},
+				{
+					"command": "gitlens.views.openTagOnRemote",
+					"when": "false"
+				},
+				{
+					"command": "gitlens.views.openTagOnRemote.multi",
+					"when": "false"
+				},
 				{
 					"command": "gitlens.copyRemoteCommitUrl",
 					"when": "gitlens:repos:withRemotes"
@@ -10572,6 +10621,14 @@
 					"command": "gitlens.views.copyRemoteCommitUrl.multi",
 					"when": "false"
 				},
+				{
+					"command": "gitlens.views.copyRemoteTagUrl",
+					"when": "false"
+				},
+				{
+					"command": "gitlens.views.copyRemoteTagUrl.multi",
+					"when": "false"
+				},
 				{
 					"command": "gitlens.openComparisonOnRemote",
 					"when": "false"
@@ -14868,6 +14925,12 @@
 					"group": "inline@99",
 					"alt": "gitlens.views.copyRemoteCommitUrl"
 				},
+				{
+					"command": "gitlens.views.openTagOnRemote",
+					"when": "gitlens:repos:withRemotes && viewItem =~ /gitlens:tag\\b(.*?\\b\\+remote\\b)/",
+					"group": "inline@99",
+					"alt": "gitlens.views.copyRemoteTagUrl"
+				},
 				{
 					"command": "gitlens.views.cherryPick",
 					"when": "!listMultiSelection && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && viewItem =~ /gitlens:commit\\b(?!.*?\\b\\+(current|rebase)\\b)/",
@@ -14984,6 +15047,16 @@
 					"when": "listMultiSelection && gitlens:repos:withRemotes && viewItem =~ /gitlens:commit\\b/",
 					"group": "3_gitlens_explore@2"
 				},
+				{
+					"command": "gitlens.views.openTagOnRemote",
+					"when": "!listMultiSelection && gitlens:repos:withRemotes && viewItem =~ /gitlens:tag\\b(.*?\\b\\+remote\\b)/",
+					"group": "3_gitlens_explore@2"
+				},
+				{
+					"command": "gitlens.views.openTagOnRemote.multi",
+					"when": "listMultiSelection && gitlens:repos:withRemotes && viewItem =~ /gitlens:tag\\b(.*?\\b\\+remote\\b)/",
+					"group": "3_gitlens_explore@2"
+				},
 				{
 					"submenu": "gitlens/share",
 					"when": "viewItem =~ /gitlens:(branch|commit|compare:(branch(?=.*?\\b\\+comparing\\b)|results(:commits(?!:)|(?!:)))|remote|repo-folder|repository|stash|status:upstream|tag|workspace|file\\b(?=.*?\\b\\+committed\\b))\\b/",
diff --git a/src/commands.ts b/src/commands.ts
index dfe74c2b285b2..6374c0ec49086 100644
--- a/src/commands.ts
+++ b/src/commands.ts
@@ -33,6 +33,7 @@ import './commands/openBranchOnRemote';
 import './commands/openCurrentBranchOnRemote';
 import './commands/openChangedFiles';
 import './commands/openCommitOnRemote';
+import './commands/openTagOnRemote';
 import './commands/openComparisonOnRemote';
 import './commands/openFileFromRemote';
 import './commands/openFileOnRemote';
diff --git a/src/commands/openOnRemote.ts b/src/commands/openOnRemote.ts
index dc05a35daa54b..96daaee7d49e7 100644
--- a/src/commands/openOnRemote.ts
+++ b/src/commands/openOnRemote.ts
@@ -196,13 +196,13 @@ export class OpenOnRemoteCommand extends Command {
 					break;
 				}
 
-				// case RemoteResourceType.Tag: {
-				// 	title = getTitlePrefix('Tag');
-				// 	if (resources.length === 1) {
-				// 		title += `${pad(GlyphChars.Dot, 2, 2)}${args.resource.tag}`;
-				// 	}
-				// 	break;
-				// }
+				case RemoteResourceType.Tag: {
+					title = getTitlePrefix('Tag');
+					if (resources.length === 1) {
+						title += `${pad(GlyphChars.Dot, 2, 2)}${resource.tag}`;
+					}
+					break;
+				}
 			}
 
 			const pick = await showRemoteProviderPicker(title, placeholder, resources, remotes, options);
diff --git a/src/commands/openTagOnRemote.ts b/src/commands/openTagOnRemote.ts
new file mode 100644
index 0000000000000..e7ebe358f84ba
--- /dev/null
+++ b/src/commands/openTagOnRemote.ts
@@ -0,0 +1,97 @@
+import type { TextEditor, Uri } from 'vscode';
+import { Commands } from '../constants.commands';
+import type { Container } from '../container';
+import { GitUri } from '../git/gitUri';
+// import { getTagNameWithoutRemote, getRemoteNameFromTagName } from '../git/models/tag';
+import { RemoteResourceType } from '../git/models/remoteResource';
+import { showGenericErrorMessage } from '../messages';
+import { CommandQuickPickItem } from '../quickpicks/items/common';
+import { ReferencesQuickPickIncludes, showReferencePicker } from '../quickpicks/referencePicker';
+import { getBestRepositoryOrShowPicker } from '../quickpicks/repositoryPicker';
+import { Logger } from '../system/logger';
+import { command, executeCommand } from '../system/vscode/command';
+import type { CommandContext } from './base';
+import { ActiveEditorCommand, getCommandUri, isCommandContextViewNodeHasTag } from './base';
+import type { OpenOnRemoteCommandArgs } from './openOnRemote';
+
+export interface OpenTagOnRemoteCommandArgs {
+	tag?: string;
+	clipboard?: boolean;
+	remote?: string;
+}
+
+@command()
+export class OpenTagOnRemoteCommand extends ActiveEditorCommand {
+	constructor(private readonly container: Container) {
+		super([Commands.OpenTagOnRemote, Commands.CopyRemoteTagUrl]);
+	}
+
+	protected override preExecute(context: CommandContext, args?: OpenTagOnRemoteCommandArgs) {
+		if (isCommandContextViewNodeHasTag(context)) {
+			args = {
+				...args,
+				tag: context.node.tag.name,
+				remote: context.node.tag.name,
+			};
+		}
+
+		if (context.command === Commands.CopyRemoteTagUrl) {
+			args = { ...args, clipboard: true };
+		}
+
+		return this.execute(context.editor, context.uri, args);
+	}
+
+	async execute(editor?: TextEditor, uri?: Uri, args?: OpenTagOnRemoteCommandArgs) {
+		uri = getCommandUri(uri, editor);
+
+		const gitUri = uri != null ? await GitUri.fromUri(uri) : undefined;
+
+		const repoPath = (
+			await getBestRepositoryOrShowPicker(
+				gitUri,
+				editor,
+				args?.clipboard ? 'Copy Remote Tag URL' : 'Open Tag On Remote',
+			)
+		)?.path;
+		if (!repoPath) return;
+
+		args = { ...args };
+
+		try {
+			if (args.tag == null) {
+				const pick = await showReferencePicker(
+					repoPath,
+					args.clipboard ? 'Copy Remote Tag URL' : 'Open Tag On Remote',
+					args.clipboard ? 'Choose a Tag to copy the URL from' : 'Choose a Tag to open',
+					{
+						autoPick: true,
+						filter: { tags: () => true, branches: () => false },
+						include: ReferencesQuickPickIncludes.Tags,
+						sort: { tags: { current: true } },
+					},
+				);
+				if (pick == null || pick instanceof CommandQuickPickItem) return;
+
+				if (pick.refType === 'tag') {
+					args.tag = pick.name;
+				} else {
+					args.tag = pick.ref;
+				}
+			}
+
+			void (await executeCommand<OpenOnRemoteCommandArgs>(Commands.OpenOnRemote, {
+				resource: {
+					type: RemoteResourceType.Tag,
+					tag: args.tag,
+				},
+				repoPath: repoPath,
+				remote: args.remote,
+				clipboard: args.clipboard,
+			}));
+		} catch (ex) {
+			Logger.error(ex, 'OpenTagOnRemoteCommand');
+			void showGenericErrorMessage('Unable to open Tag on remote provider');
+		}
+	}
+}
diff --git a/src/config.ts b/src/config.ts
index e30c6b95c9be2..ad8798ff53676 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -580,12 +580,14 @@ export interface RemotesUrlsConfig {
 	readonly fileInCommit: string;
 	readonly fileLine: string;
 	readonly fileRange: string;
+	readonly tag: string;
 }
 
 // NOTE: Must be kept in sync with `gitlens.advanced.messages` setting in the package.json
 export type SuppressedMessages =
 	| 'suppressCommitHasNoPreviousCommitWarning'
 	| 'suppressCommitNotFoundWarning'
+	| 'suppressTagNotFoundWarning'
 	| 'suppressCreatePullRequestPrompt'
 	| 'suppressDebugLoggingWarning'
 	| 'suppressFileNotUnderSourceControlWarning'
diff --git a/src/constants.commands.ts b/src/constants.commands.ts
index 0f3fca38bf62b..113e96a72b66e 100644
--- a/src/constants.commands.ts
+++ b/src/constants.commands.ts
@@ -29,6 +29,7 @@ export const enum Commands {
 	CopyRemoteBranchesUrl = 'gitlens.copyRemoteBranchesUrl',
 	CopyRemoteBranchUrl = 'gitlens.copyRemoteBranchUrl',
 	CopyRemoteCommitUrl = 'gitlens.copyRemoteCommitUrl',
+	CopyRemoteTagUrl = 'gitlens.copyRemoteTagUrl',
 	CopyRemoteComparisonUrl = 'gitlens.copyRemoteComparisonUrl',
 	CopyRemoteFileUrl = 'gitlens.copyRemoteFileUrlToClipboard',
 	CopyRemoteFileUrlWithoutRange = 'gitlens.copyRemoteFileUrlWithoutRange',
@@ -81,6 +82,7 @@ export const enum Commands {
 	OpenCurrentBranchOnRemote = 'gitlens.openCurrentBranchOnRemote',
 	OpenChangedFiles = 'gitlens.openChangedFiles',
 	OpenCommitOnRemote = 'gitlens.openCommitOnRemote',
+	OpenTagOnRemote = 'gitlens.openTagOnRemote',
 	OpenComparisonOnRemote = 'gitlens.openComparisonOnRemote',
 	OpenFileHistory = 'gitlens.openFileHistory',
 	OpenFileFromRemote = 'gitlens.openFileFromRemote',
diff --git a/src/git/models/remoteResource.ts b/src/git/models/remoteResource.ts
index fa14fc9dc93a8..1f83e271da50d 100644
--- a/src/git/models/remoteResource.ts
+++ b/src/git/models/remoteResource.ts
@@ -10,7 +10,7 @@ export const enum RemoteResourceType {
 	File = 'file',
 	Repo = 'repo',
 	Revision = 'revision',
-	// Tag = 'tag',
+	Tag = 'tag',
 }
 
 export type RemoteResource =
@@ -58,11 +58,11 @@ export type RemoteResource =
 			fileName: string;
 			range?: Range;
 			sha?: string;
+	  }
+	| {
+			type: RemoteResourceType.Tag;
+			tag: string;
 	  };
-// | {
-// 		type: RemoteResourceType.Tag;
-// 		tag: string;
-//   };
 
 export function getNameFromRemoteResource(resource: RemoteResource) {
 	switch (resource.type) {
diff --git a/src/git/remotes/azure-devops.ts b/src/git/remotes/azure-devops.ts
index c53d5872290d9..88c544a3e0d53 100644
--- a/src/git/remotes/azure-devops.ts
+++ b/src/git/remotes/azure-devops.ts
@@ -201,4 +201,8 @@ export class AzureDevOpsRemote extends RemoteProvider {
 		if (branch) return this.encodeUrl(`${this.baseUrl}/?path=/${fileName}&version=GB${branch}&_a=contents${line}`);
 		return this.encodeUrl(`${this.baseUrl}?path=/${fileName}${line}`);
 	}
+
+	protected override getUrlForTag(tag: string): string {
+		return this.encodeUrl(`${this.baseUrl}?version=GT${tag}`);
+	}
 }
diff --git a/src/git/remotes/bitbucket-server.ts b/src/git/remotes/bitbucket-server.ts
index f976c1a1be3be..e25e9d0adcde7 100644
--- a/src/git/remotes/bitbucket-server.ts
+++ b/src/git/remotes/bitbucket-server.ts
@@ -171,4 +171,8 @@ export class BitbucketServerRemote extends RemoteProvider {
 		if (branch) return `${this.encodeUrl(`${this.baseUrl}/browse/${fileName}?at=${branch}`)}${line}`;
 		return `${this.encodeUrl(`${this.baseUrl}/browse/${fileName}`)}${line}`;
 	}
+
+	protected override getUrlForTag(tag: string): string {
+		return this.encodeUrl(`${this.baseUrl}/commits/tag/${tag}`);
+	}
 }
diff --git a/src/git/remotes/bitbucket.ts b/src/git/remotes/bitbucket.ts
index 1efc4050e79c3..9423746aab00c 100644
--- a/src/git/remotes/bitbucket.ts
+++ b/src/git/remotes/bitbucket.ts
@@ -157,4 +157,8 @@ export class BitbucketRemote extends RemoteProvider {
 		if (branch) return `${this.encodeUrl(`${this.baseUrl}/src/${branch}/${fileName}`)}${line}`;
 		return `${this.encodeUrl(`${this.baseUrl}?path=${fileName}`)}${line}`;
 	}
+
+	protected override getUrlForTag(tag: string): string {
+		return this.encodeUrl(`${this.baseUrl}/commits/tag/${tag}`);
+	}
 }
diff --git a/src/git/remotes/custom.ts b/src/git/remotes/custom.ts
index 10beef3facece..e9fcf4dc438e4 100644
--- a/src/git/remotes/custom.ts
+++ b/src/git/remotes/custom.ts
@@ -100,6 +100,10 @@ export class CustomRemote extends RemoteProvider {
 		return url;
 	}
 
+	protected override getUrlForTag(tag: string): string {
+		return this.getUrl(this.urls.tag, this.getContext({ tag: tag }));
+	}
+
 	private getUrl(template: string, context: Record<string, string>): string {
 		const url = interpolate(template, context);
 		const encoded = getTokensFromTemplate(template).some(t => t.key.endsWith('_encoded'));
diff --git a/src/git/remotes/gerrit.ts b/src/git/remotes/gerrit.ts
index 5d68e06bcf5c0..7f26a933f8eaf 100644
--- a/src/git/remotes/gerrit.ts
+++ b/src/git/remotes/gerrit.ts
@@ -191,4 +191,8 @@ export class GerritRemote extends RemoteProvider {
 		if (branch) return `${this.encodeUrl(`${this.getUrlForBranch(branch)}/${fileName}`)}${line}`;
 		return `${this.encodeUrl(`${this.baseUrl}/+/HEAD/${fileName}`)}${line}`;
 	}
+
+	protected override getUrlForTag(): string | undefined {
+		return undefined;
+	}
 }
diff --git a/src/git/remotes/gitea.ts b/src/git/remotes/gitea.ts
index b2fae0ebc56c4..3ff8459c6531b 100644
--- a/src/git/remotes/gitea.ts
+++ b/src/git/remotes/gitea.ts
@@ -155,4 +155,8 @@ export class GiteaRemote extends RemoteProvider {
 		// this route is deprecated but there is no alternative
 		return `${this.encodeUrl(`${this.baseUrl}/src/${fileName}`)}${line}`;
 	}
+
+	protected getUrlForTag(tag: string): string {
+		return this.encodeUrl(`${this.baseUrl}/releases/tag/${tag}`);
+	}
 }
diff --git a/src/git/remotes/github.ts b/src/git/remotes/github.ts
index 28afc7ddbb317..e197b4154e417 100644
--- a/src/git/remotes/github.ts
+++ b/src/git/remotes/github.ts
@@ -298,6 +298,9 @@ export class GitHubRemote extends RemoteProvider<GitHubRepositoryDescriptor> {
 		if (branch) return `${this.encodeUrl(`${this.baseUrl}/blob/${branch}/${fileName}`)}${line}`;
 		return `${this.encodeUrl(`${this.baseUrl}?path=${fileName}`)}${line}`;
 	}
+	protected override getUrlForTag(tag: string) {
+		return this.encodeUrl(`${this.baseUrl}/releases/tag/${tag}`);
+	}
 }
 
 const gitHubNoReplyAddressRegex = /^(?:(\d+)\+)?([a-zA-Z\d-]{1,39})@users\.noreply\.(.*)$/i;
diff --git a/src/git/remotes/gitlab.ts b/src/git/remotes/gitlab.ts
index 39385a6a1dbf6..3bdd8a861de08 100644
--- a/src/git/remotes/gitlab.ts
+++ b/src/git/remotes/gitlab.ts
@@ -382,4 +382,8 @@ export class GitLabRemote extends RemoteProvider<GitLabRepositoryDescriptor> {
 		if (branch) return `${this.encodeUrl(`${this.baseUrl}/-/blob/${branch}/${fileName}`)}${line}`;
 		return `${this.encodeUrl(`${this.baseUrl}?path=${fileName}`)}${line}`;
 	}
+
+	protected override getUrlForTag(tag: string) {
+		return this.encodeUrl(`${this.baseUrl}/-/tags/${tag}`);
+	}
 }
diff --git a/src/git/remotes/remoteProvider.ts b/src/git/remotes/remoteProvider.ts
index c36e491947d2c..0e5d934dfd328 100644
--- a/src/git/remotes/remoteProvider.ts
+++ b/src/git/remotes/remoteProvider.ts
@@ -137,9 +137,8 @@ export abstract class RemoteProvider<T extends ResourceDescriptor = ResourceDesc
 					resource.sha != null ? resource.sha : undefined,
 					resource.range,
 				);
-			// TODO@axosoft-ramint needs to be implemented to support remote urls for tags
-			// case RemoteResourceType.Tag:
-			// 	return this.getUrlForTag(resource.tag);
+			case RemoteResourceType.Tag:
+				return this.getUrlForTag(resource.tag);
 			default:
 				return undefined;
 		}
@@ -175,6 +174,7 @@ export abstract class RemoteProvider<T extends ResourceDescriptor = ResourceDesc
 	): string | undefined;
 
 	protected abstract getUrlForFile(fileName: string, branch?: string, sha?: string, range?: Range): string;
+	protected abstract getUrlForTag(tag: string): string | undefined;
 
 	protected getUrlForRepository(): string {
 		return this.baseUrl;
diff --git a/src/messages.ts b/src/messages.ts
index 3d2977a26468d..94e373ec0d8bc 100644
--- a/src/messages.ts
+++ b/src/messages.ts
@@ -42,7 +42,11 @@ export function showCommitHasNoPreviousCommitWarningMessage(commit?: GitCommit):
 }
 
 export function showCommitNotFoundWarningMessage(message: string): Promise<MessageItem | undefined> {
-	return showMessage('warn', `${message}. The commit could not be found.`, 'suppressCommitNotFoundWarning');
+	return showMessage('warn', `${message}. The tag commit not be found.`, 'suppressCommitNotFoundWarning');
+}
+
+export function showTagNotFoundWarningMessage(message: string): Promise<MessageItem | undefined> {
+	return showMessage('warn', `${message}. The tag could not be found.`, 'suppressTagNotFoundWarning');
 }
 
 export async function showCreatePullRequestPrompt(branch: string): Promise<boolean> {
diff --git a/src/views/nodes/tagNode.ts b/src/views/nodes/tagNode.ts
index 77cb73bd574c9..ca996b53bbee8 100644
--- a/src/views/nodes/tagNode.ts
+++ b/src/views/nodes/tagNode.ts
@@ -26,6 +26,7 @@ export class TagNode extends ViewRefNode<'tag', ViewsWithTags, GitTagReference>
 		view: ViewsWithTags,
 		public override parent: ViewNode,
 		public readonly tag: GitTag,
+		public readonly remoteUrl: string | undefined,
 	) {
 		super('tag', uri, view, parent);
 
@@ -81,7 +82,11 @@ export class TagNode extends ViewRefNode<'tag', ViewsWithTags, GitTagReference>
 	getTreeItem(): TreeItem {
 		const item = new TreeItem(this.label, TreeItemCollapsibleState.Collapsed);
 		item.id = this.id;
-		item.contextValue = ContextValues.Tag;
+		let contextValue: string = ContextValues.Tag;
+		if (this.remoteUrl) {
+			contextValue += '+remote';
+		}
+		item.contextValue = contextValue;
 		item.description = emojify(this.tag.message);
 		item.tooltip = `${this.tag.name}${pad(GlyphChars.Dash, 2, 2)}${shortenRevision(this.tag.sha, {
 			force: true,
diff --git a/src/views/nodes/tagsNode.ts b/src/views/nodes/tagsNode.ts
index bd2a42d2f1ef5..26371063094ad 100644
--- a/src/views/nodes/tagsNode.ts
+++ b/src/views/nodes/tagsNode.ts
@@ -1,5 +1,6 @@
 import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
 import { GitUri } from '../../git/gitUri';
+import { RemoteResourceType } from '../../git/models/remoteResource';
 import type { Repository } from '../../git/models/repository';
 import { makeHierarchical } from '../../system/array';
 import { debug } from '../../system/decorators/log';
@@ -36,10 +37,17 @@ export class TagsNode extends CacheableChildrenViewNode<'tags', ViewsWithTagsNod
 		if (this.children == null) {
 			const tags = await this.repo.git.getTags({ sort: true });
 			if (tags.values.length === 0) return [new MessageNode(this.view, this, 'No tags could be found.')];
-
+			const remote = await this.repo.git.getBestRemoteWithProvider();
 			// TODO@eamodio handle paging
 			const tagNodes = tags.values.map(
-				t => new TagNode(GitUri.fromRepoPath(this.uri.repoPath!, t.ref), this.view, this, t),
+				t =>
+					new TagNode(
+						GitUri.fromRepoPath(this.uri.repoPath!, t.ref),
+						this.view,
+						this,
+						t,
+						remote?.provider?.url({ type: RemoteResourceType.Tag, tag: t.name }),
+					),
 			);
 			if (this.view.config.branches.layout === 'list') return tagNodes;
 
diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts
index 44111c845bff6..a8b7c123d45ed 100644
--- a/src/views/viewCommands.ts
+++ b/src/views/viewCommands.ts
@@ -264,6 +264,22 @@ export class ViewCommands implements Disposable {
 				(n, nodes) => this.openCommitOnRemote(n, nodes),
 				this,
 			),
+			registerViewCommand('gitlens.views.openTagOnRemote', (n, nodes) => this.openTagOnRemote(n, nodes), this),
+			registerViewCommand(
+				'gitlens.views.openTagOnRemote.multi',
+				(n, nodes) => this.openTagOnRemote(n, nodes),
+				this,
+			),
+			registerViewCommand(
+				'gitlens.views.copyRemoteTagUrl',
+				(n, nodes) => this.openTagOnRemote(n, nodes, true),
+				this,
+			),
+			registerViewCommand(
+				'gitlens.views.copyRemoteTagUrl.multi',
+				(n, nodes) => this.openTagOnRemote(n, nodes, true),
+				this,
+			),
 
 			registerViewCommand('gitlens.views.openChanges', this.openChanges, this),
 			registerViewCommand('gitlens.views.openChangesWithWorking', this.openChangesWithWorking, this),
@@ -1333,6 +1349,17 @@ export class ViewCommands implements Disposable {
 		});
 	}
 
+	@log()
+	private openTagOnRemote(node: ViewRefNode, nodes?: ViewRefNode[], clipboard?: boolean) {
+		const refs = nodes?.length ? nodes.map(n => n.ref) : [node.ref];
+
+		return executeCommand<OpenOnRemoteCommandArgs>(Commands.OpenOnRemote, {
+			repoPath: refs[0].repoPath,
+			resource: refs.map(r => ({ type: RemoteResourceType.Tag, tag: r.name })),
+			clipboard: clipboard,
+		});
+	}
+
 	@log()
 	private openChanges(node: ViewRefFileNode | MergeConflictFileNode | StatusFileNode) {
 		if (node.is('conflict-file')) {