diff --git a/ghost/core/core/server/api/endpoints/comments-members.js b/ghost/core/core/server/api/endpoints/comments-members.js
index 49cb949882..8a7c025535 100644
--- a/ghost/core/core/server/api/endpoints/comments-members.js
+++ b/ghost/core/core/server/api/endpoints/comments-members.js
@@ -1,18 +1,7 @@
-const Promise = require('bluebird');
-const tpl = require('@tryghost/tpl');
-const errors = require('@tryghost/errors');
-const models = require('../../models');
 const commentsService = require('../../services/comments');
 const ALLOWED_INCLUDES = ['member', 'replies', 'replies.member', 'replies.count.likes', 'replies.liked', 'count.replies', 'count.likes', 'liked', 'post', 'parent'];
 const UNSAFE_ATTRS = ['status'];
 
-const messages = {
-    commentNotFound: 'Comment could not be found',
-    memberNotFound: 'Unable to find member',
-    likeNotFound: 'Unable to find like',
-    alreadyLiked: 'This comment was liked already'
-};
-
 module.exports = {
     docName: 'comments',
 
@@ -157,27 +146,7 @@ module.exports = {
         },
         permissions: true,
         async query(frame) {
-            // TODO: move to likes service
-            if (frame.options?.context?.member?.id) {
-                const data = {
-                    member_id: frame.options.context.member.id,
-                    comment_id: frame.options.id
-                };
-
-                const existing = await models.CommentLike.findOne(data, frame.options);
-
-                if (existing) {
-                    throw new errors.BadRequestError({
-                        message: tpl(messages.alreadyLiked)
-                    });
-                }
-
-                return await models.CommentLike.add(data, frame.options);
-            } else {
-                throw new errors.NotFoundError({
-                    message: tpl(messages.memberNotFound)
-                });
-            }
+            return await commentsService.controller.like(frame);
         }
     },
 
@@ -188,31 +157,8 @@ module.exports = {
         ],
         validation: {},
         permissions: true,
-        query(frame) {
-            // TODO: move to likes service
-            if (frame.options?.context?.member?.id) {
-                return models.CommentLike.destroy({
-                    ...frame.options,
-                    destroyBy: {
-                        member_id: frame.options.context.member.id,
-                        comment_id: frame.options.id
-                    },
-                    require: true
-                }).then(() => null)
-                    .catch((err) => {
-                        if (err instanceof models.CommentLike.NotFoundError) {
-                            return Promise.reject(new errors.NotFoundError({
-                                message: tpl(messages.likeNotFound)
-                            }));
-                        }
-
-                        throw err;
-                    });
-            } else {
-                return Promise.reject(new errors.NotFoundError({
-                    message: tpl(messages.memberNotFound)
-                }));
-            }
+        async query(frame) {
+            return await commentsService.controller.unlike(frame);
         }
     },
 
@@ -224,13 +170,7 @@ module.exports = {
         validation: {},
         permissions: true,
         async query(frame) {
-            if (!frame.options?.context?.member?.id) {
-                return Promise.reject(new errors.UnauthorizedError({
-                    message: tpl(messages.memberNotFound)
-                }));
-            }
-
-            await commentsService.api.reportComment(frame.options.id, frame.options?.context?.member);
+            await commentsService.controller.report(frame);
         }
     }
 };
diff --git a/ghost/core/core/server/services/comments/controller.js b/ghost/core/core/server/services/comments/controller.js
index 30e14d2372..18f9182a2c 100644
--- a/ghost/core/core/server/services/comments/controller.js
+++ b/ghost/core/core/server/services/comments/controller.js
@@ -1,4 +1,5 @@
 const _ = require('lodash');
+const errors = require('@tryghost/errors');
 
 /**
  * @typedef {import('../../api/shared/frame')} Frame
@@ -8,7 +9,8 @@ const {MethodNotAllowedError} = require('@tryghost/errors');
 const tpl = require('@tryghost/tpl');
 
 const messages = {
-    cannotDestroyComments: 'You cannot destroy comments.'
+    cannotDestroyComments: 'You cannot destroy comments.',
+    memberNotFound: 'Unable to find member'
 };
 
 module.exports = class CommentsController {
@@ -21,6 +23,14 @@ module.exports = class CommentsController {
         this.stats = stats;
     }
 
+    #checkMember(frame) {
+        if (!frame.options?.context?.member?.id) {
+            throw new errors.UnauthorizedError({
+                message: tpl(messages.memberNotFound)
+            });
+        }
+    }
+
     /**
      * @param {Frame} frame
      */
@@ -46,6 +56,8 @@ module.exports = class CommentsController {
      * @param {Frame} frame
      */
     async edit(frame) {
+        this.#checkMember(frame);
+
         if (frame.data.comments[0].status === 'deleted') {
             return await this.service.deleteComment(
                 frame.options.id,
@@ -66,6 +78,7 @@ module.exports = class CommentsController {
      * @param {Frame} frame
      */
     async add(frame) {
+        this.#checkMember(frame);
         const data = frame.data.comments[0];
 
         if (data.parent_id) {
@@ -98,4 +111,42 @@ module.exports = class CommentsController {
 
         return await this.stats.getCountsByPost(frame.data.ids);
     }
+
+    /**
+     * @param {Frame} frame
+     */
+    async like(frame) {
+        this.#checkMember(frame);
+
+        return await this.service.likeComment(
+            frame.options.id, 
+            frame.options?.context?.member, 
+            frame.options
+        );
+    }
+
+    /**
+     * @param {Frame} frame
+     */
+    async unlike(frame) {
+        this.#checkMember(frame);
+
+        return await this.service.unlikeComment(
+            frame.options.id, 
+            frame.options?.context?.member, 
+            frame.options
+        );
+    }
+
+    /**
+     * @param {Frame} frame
+     */
+    async report(frame) {
+        this.#checkMember(frame);
+
+        return await this.service.reportComment(
+            frame.options.id, 
+            frame.options?.context?.member
+        );
+    }
 };
diff --git a/ghost/core/core/server/services/comments/service.js b/ghost/core/core/server/services/comments/service.js
index cd1d4463c9..3bfba50087 100644
--- a/ghost/core/core/server/services/comments/service.js
+++ b/ghost/core/core/server/services/comments/service.js
@@ -87,6 +87,58 @@ class CommentsService {
         }
     }
 
+    async likeComment(commentId, member, options = {}) {
+        this.checkEnabled();
+
+        const memberModel = await this.models.Member.findOne({
+            id: member.id
+        }, {
+            require: true,
+            ...options,
+            withRelated: ['products']
+        });
+
+        this.checkCommentAccess(memberModel);
+
+        const data = {
+            member_id: memberModel.id,
+            comment_id: commentId
+        };
+
+        const existing = await this.models.CommentLike.findOne(data, options);
+
+        if (existing) {
+            throw new errors.BadRequestError({
+                message: tpl(messages.alreadyLiked)
+            });
+        }
+
+        return await this.models.CommentLike.add(data, options);
+    }
+
+    async unlikeComment(commentId, member, options = {}) {
+        this.checkEnabled();
+
+        try {
+            await this.models.CommentLike.destroy({
+                ...options,
+                destroyBy: {
+                    member_id: member.id,
+                    comment_id: commentId
+                },
+                require: true
+            });
+        } catch (err) {
+            if (err instanceof this.models.CommentLike.NotFoundError) {
+                return Promise.reject(new errors.NotFoundError({
+                    message: tpl(messages.likeNotFound)
+                }));
+            }
+
+            throw err;
+        }
+    }
+
     async reportComment(commentId, reporter) {
         this.checkEnabled();
         const comment = await this.models.Comment.findOne({id: commentId}, {require: true});
diff --git a/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap b/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap
index b843912e49..52bb618838 100644
--- a/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap
+++ b/ghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snap
@@ -3093,6 +3093,96 @@ Object {
 }
 `;
 
+exports[`Comments API when commenting enabled for all when not authenticated cannot comment on a post 1: [body] 1`] = `
+Object {
+  "errors": Array [
+    Object {
+      "code": null,
+      "context": "Unable to find member",
+      "details": null,
+      "ghostErrorCode": null,
+      "help": null,
+      "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
+      "message": "Authorisation error, cannot save comment.",
+      "property": null,
+      "type": "UnauthorizedError",
+    },
+  ],
+}
+`;
+
+exports[`Comments API when commenting enabled for all when not authenticated cannot comment on a post 2: [headers] 1`] = `
+Object {
+  "access-control-allow-origin": "*",
+  "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
+  "content-length": "250",
+  "content-type": "application/json; charset=utf-8",
+  "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
+  "vary": "Accept-Encoding",
+  "x-powered-by": "Express",
+}
+`;
+
+exports[`Comments API when commenting enabled for all when not authenticated cannot like a comment 1: [body] 1`] = `
+Object {
+  "errors": Array [
+    Object {
+      "code": null,
+      "context": null,
+      "details": null,
+      "ghostErrorCode": null,
+      "help": null,
+      "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
+      "message": "Unable to find member",
+      "property": null,
+      "type": "UnauthorizedError",
+    },
+  ],
+}
+`;
+
+exports[`Comments API when commenting enabled for all when not authenticated cannot like a comment 2: [headers] 1`] = `
+Object {
+  "access-control-allow-origin": "*",
+  "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
+  "content-length": "211",
+  "content-type": "application/json; charset=utf-8",
+  "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
+  "vary": "Accept-Encoding",
+  "x-powered-by": "Express",
+}
+`;
+
+exports[`Comments API when commenting enabled for all when not authenticated cannot reply on a post 1: [body] 1`] = `
+Object {
+  "errors": Array [
+    Object {
+      "code": null,
+      "context": "Unable to find member",
+      "details": null,
+      "ghostErrorCode": null,
+      "help": null,
+      "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
+      "message": "Authorisation error, cannot save comment.",
+      "property": null,
+      "type": "UnauthorizedError",
+    },
+  ],
+}
+`;
+
+exports[`Comments API when commenting enabled for all when not authenticated cannot reply on a post 2: [headers] 1`] = `
+Object {
+  "access-control-allow-origin": "*",
+  "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
+  "content-length": "250",
+  "content-type": "application/json; charset=utf-8",
+  "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
+  "vary": "Accept-Encoding",
+  "x-powered-by": "Express",
+}
+`;
+
 exports[`Comments API when commenting enabled for all when not authenticated cannot report a comment 1: [body] 1`] = `
 Object {
   "errors": Array [
@@ -3123,6 +3213,36 @@ Object {
 }
 `;
 
+exports[`Comments API when commenting enabled for all when not authenticated cannot unlike a comment 1: [body] 1`] = `
+Object {
+  "errors": Array [
+    Object {
+      "code": null,
+      "context": null,
+      "details": null,
+      "ghostErrorCode": null,
+      "help": null,
+      "id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
+      "message": "Unable to find member",
+      "property": null,
+      "type": "UnauthorizedError",
+    },
+  ],
+}
+`;
+
+exports[`Comments API when commenting enabled for all when not authenticated cannot unlike a comment 2: [headers] 1`] = `
+Object {
+  "access-control-allow-origin": "*",
+  "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
+  "content-length": "211",
+  "content-type": "application/json; charset=utf-8",
+  "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
+  "vary": "Accept-Encoding",
+  "x-powered-by": "Express",
+}
+`;
+
 exports[`Comments API when not authenticated but enabled Can browse all comments of a post 1: [body] 1`] = `
 Object {
   "comments": Array [
diff --git a/ghost/core/test/e2e-api/members-comments/comments.test.js b/ghost/core/test/e2e-api/members-comments/comments.test.js
index 0315427631..2a140da383 100644
--- a/ghost/core/test/e2e-api/members-comments/comments.test.js
+++ b/ghost/core/test/e2e-api/members-comments/comments.test.js
@@ -142,14 +142,14 @@ async function testCanReply(member, emailMatchers = {}) {
     should.notEqual(member.get('last_commented_at').getTime(), date.getTime(), 'Should update `last_commented_at` property after posting a comment.');
 }
 
-async function testCannotCommentOnPost() {
+async function testCannotCommentOnPost(status = 403) {
     await membersAgent
         .post(`/api/comments/`)
         .body({comments: [{
             post_id: postId,
             html: '<div></div><p></p><p>This is a <strong>message</strong></p><p></p><p></p><p>New line</p><p></p>'
         }]})
-        .expectStatus(403)
+        .expectStatus(status)
         .matchHeaderSnapshot({
             etag: anyEtag
         })
@@ -160,7 +160,7 @@ async function testCannotCommentOnPost() {
         });
 }
 
-async function testCannotReply() {
+async function testCannotReply(status = 403) {
     await membersAgent
         .post(`/api/comments/`)
         .body({comments: [{
@@ -168,7 +168,7 @@ async function testCannotReply() {
             parent_id: fixtureManager.get('comments', 0).id,
             html: 'This is a reply'
         }]})
-        .expectStatus(403)
+        .expectStatus(status)
         .matchHeaderSnapshot({
             etag: anyEtag
         })
@@ -228,6 +228,14 @@ describe('Comments API', function () {
                         comments: [commentMatcherWithReplies({replies: 1})]
                     });
             });
+
+            it('cannot comment on a post', async function () {
+                await testCannotCommentOnPost(401);
+            });
+
+            it('cannot reply on a post', async function () {
+                await testCannotReply(401);
+            });
     
             it('cannot report a comment', async function () {
                 commentId = fixtureManager.get('comments', 0).id;
@@ -245,6 +253,36 @@ describe('Comments API', function () {
                         }]
                     });
             });
+
+            it('cannot like a comment', async function () {
+                // Create a temporary comment
+                await membersAgent
+                    .post(`/api/comments/${commentId}/like/`)
+                    .expectStatus(401)
+                    .matchHeaderSnapshot({
+                        etag: anyEtag
+                    })
+                    .matchBodySnapshot({
+                        errors: [{
+                            id: anyUuid
+                        }]
+                    });
+            });
+
+            it('cannot unlike a comment', async function () {
+                // Create a temporary comment
+                await membersAgent
+                    .delete(`/api/comments/${commentId}/like/`)
+                    .expectStatus(401)
+                    .matchHeaderSnapshot({
+                        etag: anyEtag
+                    })
+                    .matchBodySnapshot({
+                        errors: [{
+                            id: anyUuid
+                        }]
+                    });
+            });
         });
 
         describe('when authenticated', function () {